在NX(原Unigraphics)二次开发中,Block UI是西门子提供的一套现代化界面开发框架,它允许开发者通过C++或.NET语言创建与NX深度集成的自定义对话框。其中Specify Point(指定点)控件是交互式获取三维坐标的关键组件,广泛用于需要用户手动选取模型位置的功能开发。
这个控件的典型应用场景包括:
实际开发中最大的痛点在于:虽然NX Open API文档提供了基础用法说明,但关于如何正确获取和处理点坐标数据、如何处理用户取消操作等边界情况,往往需要开发者通过大量试错才能掌握可靠方案。本文将基于C++实现,详解一整套经过生产验证的解决方案。
xml复制<property name="point" type="point3d">
<value>0,0,0</value>
</property>
mermaid复制classDiagram
NXOpen::BlockStyler::Block* <|-- SpecifyPoint
SpecifyPoint : +GetPoint()
SpecifyPoint : +SetPoint()
SpecifyPoint : +RegisterValueChangedHandler()
实际生成的控件类继承自NXOpen::BlockStyler::Block,核心方法包括:
GetProperties() 获取控件当前所有属性GetPoint() 返回NXOpen::Point3d对象SetPoint() 以编程方式设置点坐标AddValueChangedHandler() 注册值变更回调关键提示:在NX 1980系列版本后,建议使用
dynamic_cast进行类型转换而非直接强制转换,可避免内存异常。
在对话框类的初始化方法中需要完成:
cpp复制// 头文件包含
#include <uf_defs.h>
#include <NXOpen/BlockStyler_SpecifyPoint.hxx>
// 类成员声明
NXOpen::BlockStyler::SpecifyPoint* m_pointCtrl;
// 初始化代码片段
void MyDialog::Initialize()
{
m_pointCtrl = dynamic_cast<NXOpen::BlockStyler::SpecifyPoint*>(
m_blockDialog->GetBlock("specify_point1")); // 匹配设计器中的控件ID
// 设置初始值为绝对坐标系原点
NXOpen::Point3d origin(0.0, 0.0, 0.0);
m_pointCtrl->SetPoint(origin);
// 注册值变更回调
m_pointCtrl->AddValueChangedHandler(
make_callback(this, &MyDialog::OnPointChanged));
}
根据不同的业务需求,点坐标可能需要以不同形式获取:
| 获取方式 | 适用场景 | 代码示例 |
|---|---|---|
| 绝对坐标 | CNC加工定位 | point.GetCoordinates() |
| 相对坐标 | 增量移动操作 | point - refPoint |
| 特征参数化坐标 | 关联设计变更 | point.GetFeatureParameters() |
典型坐标获取实现:
cpp复制void MyDialog::OnApply()
{
try {
NXOpen::Point3d currentPoint = m_pointCtrl->GetPoint();
double x = currentPoint.X;
double y = currentPoint.Y;
double z = currentPoint.Z;
// 转换为工作坐标系
NXOpen::Part* workPart = theSession->Parts()->Work();
NXOpen::Point3d wcsPoint = workPart->WCS()->ConvertCoordinates(
currentPoint,
NXOpen::Part::CoordinateSystemAbsolute,
NXOpen::Part::CoordinateSystemWork);
// 业务逻辑处理...
}
catch (const NXOpen::NXException& e) {
theUI->NXMessageBox()->Show("错误", NXOpen::NXMessageBox::DialogTypeError, e.Message());
}
}
cpp复制if (m_pointCtrl->GetPoint().IsNull())
{
// 处理未选择点的情况
}
cpp复制// 将毫米转换为英寸(假设当前NX会话使用毫米制)
double xInch = currentPoint.X / 25.4;
cpp复制#if NX_VERSION >= 1980
// 新版API
#else
// 旧版兼容代码
#endif
通过UFUN函数实现选择过滤:
cpp复制#include <uf_ui.h>
#include <uf_obj.h>
void SetPointFilter()
{
tag_t filter;
UF_UI_create_selection_filter(&filter,
UF_UI_SEL_FEATURE_ANY_FACE | UF_UI_SEL_FEATURE_ANY_EDGE);
UF_UI_set_sel_filter(m_pointCtrl->Tag(), filter);
}
在对话框的SaveValues方法中实现:
cpp复制void MyDialog::SaveValues()
{
NXOpen::Point3d point = m_pointCtrl->GetPoint();
theSession->UpdateManager()->AddToDeleteList(m_storedPoint);
m_storedPoint = theSession->Parts()->Work()->Points()->CreatePoint(point);
}
cpp复制m_pointCtrl->BeginUpdate();
// 批量设置多个属性...
m_pointCtrl->EndUpdate();
cpp复制m_pointCtrl->SetUpdateHandler(
make_callback(this, &MyDialog::OnPointUpdate),
NXOpen::BlockStyler::Block::UpdateTypeAsynchronous);
Preferences->Units设置ConvertCoordinates转换UF_UI_reset_sel_filter()使用NXOpen内存调试模式:
bat复制set UGII_MEMORY_DEBUG=1
set UGII_MEMORY_DEBUG_DUMP=1
cpp复制// 使用NX内部精度比较
bool IsEqual(const NXOpen::Point3d& p1, const NXOpen::Point3d& p2)
{
return (fabs(p1.X - p2.X) < 1e-6) &&
(fabs(p1.Y - p2.Y) < 1e-6) &&
(fabs(p1.Z - p2.Z) < 1e-6);
}
cpp复制// 在设计器中设置本地化标签
<property name="label" type="string">
<value lang="en">Specify Point</value>
<value lang="zh-cn">指定点</value>
</property>
cpp复制TEST_F(PointControlTest, TestNullPoint)
{
m_pointCtrl->SetPoint(NXOpen::Point3d::Null());
ASSERT_TRUE(m_pointCtrl->GetPoint().IsNull());
}
在实际项目中,我们通过封装PointControlWrapper类,整合了上述所有最佳实践,显著提高了开发效率和代码健壮性。核心封装接口包括: