ObjectARX自定义对象的OPM

news/2024/11/24 9:08:36/

        在AutoCAD绘图工作中,经常用到特性面板,它可以方便地查询、修改CAD对象的详细信息,如颜色、线型等,它是一个非常实用且便捷的工具。如果能为自定义对象添加一个特性面板(OPM),这无疑是众多初学者极感兴趣的事情。笔者也是一名ObjectARX的初学者和爱好者,为了给自定义对象添加OPM,笔者查阅了无数的网上和书籍资料,但大多寥寥数语,含糊其辞。功夫不负有心人,在经历了千百次的“测试-失败-改进”后,终于摸索出了自定义对象OPM编程的部分细节,现将之分享。由于笔者水平有限,文中的错误和疏漏还望各位大侠指点。

         本文以具有6个顶点的折线及一行格式化文字组成一个自定义对象(MyObject),拟在特性面板中实现折线顶点的显示与编辑、格式化文本的选择以及位置编辑等功能。笔者所用版本为ObjectARX 2020,编程语言为C++。本例功能虽简单,但特性表中的一般项、下拉列表控件、数值调节控件、三维坐标分解等均有涉及,基本可以涵盖特性表的全部常用功能。

  1. 添加新建项,双击“ArxAtlWizComWrapper”;
  2. Shot Name填写“MyObjectOPM”,其他默认,点击下一步;
  3. DBX classname填写自定义对象的类名“MyObject”,并选中“Entity Interfafe support(versus just Object)”、“Use IOPMPropertyExtensionImpl”、“Implement IOPMPropertyExpander”选项,点击完成;
  4. 打开“MyObjectOPM.h”文件,将#include "***.h"(DBX类的头文件)修改为#include "***_i.h",并添加包含#include "MyObject.h";
  5. 重新生成一次。
  6. 为自定义对象类添加函数:virtual Acad::ErrorStatus subGetClassID(CLSID* pClsid) const(protected属性),其实现代码如下:
//获取OPM的ID,以建立COM方式的通讯
Acad::ErrorStatus MyObject::subGetClassID(CLSID* pClsid) const
{assertReadEnabled();*pClsid = CLSID_MyObjectOPM;return Acad::eOk;
}

 7. 变量CLSID_MyObjectOPM会报错,添加4中的包含文件:#include "***_i.h"

8. 为自定义对象类添加私有变量:

private:AcGePoint3d			mTxPt;	//格式化文本插入点AcGePoint3dArray	mPts;	//折线的顶点坐标集合AcString			mTx;	//格式化文本

 9. 为自定义对象类添加公有函数:

//-----------------------------------------------------------------------------
Acad::ErrorStatus MyObject::GetVertex(int index, AcGePoint3d &pt) const
{assertReadEnabled();pt = mPts[index];return Acad::eOk;
}//-----------------------------------------------------------------------------
Acad::ErrorStatus MyObject::SetVertex(int index, AcGePoint3d newVal)
{assertWriteEnabled();mPts[index] = newVal;return Acad::eOk;
}//-----------------------------------------------------------------------------
AcString MyObject::GetText() const
{assertReadEnabled();return mTx;
}//-----------------------------------------------------------------------------
Acad::ErrorStatus MyObject::SetText(AcString newVal)
{assertWriteEnabled();mTx = newVal;return Acad::eOk;
}//-----------------------------------------------------------------------------
Acad::ErrorStatus MyObject::GetIns(AcGePoint3d &pt) const
{assertReadEnabled();pt = mTxPt;return Acad::eOk;
}//-----------------------------------------------------------------------------
Acad::ErrorStatus MyObject::SetIns(AcGePoint3d newVal)
{assertWriteEnabled();mTxPt = newVal;return Acad::eOk;
}

 10. 打开DBX项目的.idl文件,在interface IMyObjectOPM : IAcadEntity   { };段中添加如下代码,以初始化特性表:

[propget, id(1), helpstring("折线顶点坐标集合")] HRESULT Vertex([in] SHORT index, [out, retval] VARIANT *pVal);
[propput, id(1), helpstring("折线顶点坐标集合")] HRESULT Vertex([in] SHORT index, [in] VARIANT newVal);
[propget, id(2), helpstring("格式化的文本")] HRESULT Explain([out, retval] BSTR *pVal);
[propput, id(2), helpstring("格式化的文本")] HRESULT Explain([in] BSTR *newVal);
[propget, id(3), helpstring("文本插入点")] HRESULT InsertPt([out, retval] VARIANT *pVal);
[propput, id(3), helpstring("文本插入点")] HRESULT InsertPt([in] VARIANT newVal);
[propget, id(4), helpstring("说明")] HRESULT Note([out, retval] BSTR *pVal);
//注意:id值从1开始自定义编号,既是在“特性”列表中的显示顺序(dispID),但此值在所有代码中须始终一一对应。

11. 打开“MyObjectOPM.h”文件,定义2个常量:#define WjcCategoryID1 86  #define WjcCategoryID2 87//用于特性表的分类号,在BEGIN_OPMPROP_MAP() END_OPMPROP_MAP()段间插入如下语句,以进行属性分类及编辑模式等的声明,这些语句须和10中的语句对应:

//IOPMPropertyExtension
BEGIN_OPMPROP_MAP()OPMPROP_ENTRY(0, 0x001, WjcCategoryID1, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")OPMPROP_ENTRY(0, 0x002, WjcCategoryID2, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")OPMPROP_ENTRY(0, 0x003, WjcCategoryID2, 0, 0, 0, _T(""), 0, 1, IID_NULL, IID_NULL, "")OPMPROP_ENTRY(0, 0x004, WjcCategoryID1, 0, 0, 0, _T(""), 0, 0, IID_NULL, IID_NULL, "")
END_OPMPROP_MAP()
/*⑴DescriptionID:默认0
⑵dispID:见10条的注意
⑶catagoryID:分组的ID
⑷catagoryNameID:默认0
⑸elements string list ID (semi-colon separator) :默认0
⑹predefined strings ID (semi-colon separator) :默认0
⑺predefined values:默认_T("")
⑻grouping:默认0
⑼editable:是否可编辑:1-可编辑;0-不能编辑
⑽property:默认 IID_NULL
⑾other:默认 IID_NULL
⑿proppage:默认 ""*/

 12. 声明如下函数,这些函数须和10中的语句一一对应,且函数名称、参数都是一一对应的,这些函数是OPM和自定义对象进行数据交换的唯一通道:

public://IMyObjectOPMSTDMETHOD(get_Vertex)(/*[in]*/ SHORT index, /*[out, retval]*/VARIANT *pVal);STDMETHOD(put_Vertex)(/*[in]*/ SHORT index, /*[in]*/ VARIANT newVal);STDMETHOD(get_Explain)(BSTR *pVal);STDMETHOD(put_Explain)(BSTR *newVal);STDMETHOD(get_InsertPt)(/*[out, retval]*/VARIANT *pVal);STDMETHOD(put_InsertPt)(/*[in]*/ VARIANT newVal);STDMETHOD(get_Note)(BSTR *pVal);//在MyObject类中须有与这些函数对应的函数

13. 实现上述函数:

//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::get_Vertex(/*[in]*/ SHORT index, /*[out, retval]*/VARIANT *pVal)
{try{AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForRead);if (pC.openStatus() != Acad::eOk)return E_ACCESSDENIED;AcAxPoint3d pt;pC->GetVertex(index, pt);pt.setVariant(*pVal);}catch (const HRESULT hf)//{Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::put_Vertex(/*[in]*/ SHORT index, /*[in]*/ VARIANT newVal)
{try{AcAxDocLock docLock(m_objRef.objectId(), AcAxDocLock::kNormal);if (docLock.lockStatus() != Acad::eOk && docLock.lockStatus() != Acad::eNoDatabase)return E_ACCESSDENIED;AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForWrite);if (pC.openStatus() != Acad::eOk){return E_ACCESSDENIED;}AcAxPoint3d pt(newVal);pC->SetVertex(index, pt);}catch (const HRESULT hf){Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::get_Explain(BSTR *pVal)
{try{AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForRead);if (pC.openStatus() != Acad::eOk){return E_ACCESSDENIED;}*pVal = ::SysAllocString(pC->GetText());}catch (const HRESULT hf){Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::put_Explain(BSTR *newVal)
{try{AcString ts;ts.format(_T("%s"), *newVal);::SysFreeString(*newVal);AcAxDocLock docLock(m_objRef.objectId(), AcAxDocLock::kNormal);if (docLock.lockStatus() != Acad::eOk && docLock.lockStatus() != Acad::eNoDatabase)return E_ACCESSDENIED;AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForWrite);if (pC.openStatus() != Acad::eOk){return E_ACCESSDENIED;}pC->SetText(ts);}catch (const HRESULT hf){Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::get_InsertPt(/*[out, retval]*/VARIANT *pVal)
{try{AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForRead);if (pC.openStatus() != Acad::eOk)return E_ACCESSDENIED;AcAxPoint3d pt;pC->GetIns(pt);pt.setVariant(*pVal);}catch (const HRESULT hf)//{Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::put_InsertPt(/*[in]*/ VARIANT newVal)
{try{AcAxDocLock docLock(m_objRef.objectId(), AcAxDocLock::kNormal);if (docLock.lockStatus() != Acad::eOk && docLock.lockStatus() != Acad::eNoDatabase)return E_ACCESSDENIED;AcDbObjectPointer <MyObject>pC(m_objRef.objectId(), AcDb::kForWrite);if (pC.openStatus() != Acad::eOk){return E_ACCESSDENIED;}AcAxPoint3d pt(newVal);pC->SetIns(pt);}catch (const HRESULT hf){Error("请检查输入的参数!");return hf;}return S_OK;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::get_Note(BSTR *pVal)
{*pVal = ::SysAllocString(_T("成功展示"));return S_OK;
}

 14. 在“MyObjectOPM.h”中声明如下重载函数(public):

    //IOPMPropertyExpander//分解复杂的属性(如三维坐标值包含x、y、z属性)STDMETHOD(GetElementStrings)(/*[in]*/DISPID dispID, /*[out]*/OPMLPOLESTR __RPC_FAR *pCaStringsOut, /*[out]*/OPMDWORD __RPC_FAR *pCaCookiesOut);//设置复杂属性的当前值STDMETHOD(GetElementValue)(/*[in]*/DISPID dispID, /*[in]*/DWORD dwCookie, /*[out]*/VARIANT *pVarOut);//读取复杂属性的当前值,并返回给关联的实体STDMETHOD(SetElementValue)(/*[in]*/DISPID dispID, /*[in]*/DWORD dwCookie, /*[in]*/VARIANT VarIn);//设置数值调节钮控件每组数据的数据个数,如三维坐标的每组数据个数为3,即x、y、zSTDMETHOD(GetElementGrouping)(/*[in]*/DISPID dispID, /*[out]*/short *groupingNumber);//设置数值调节钮控件的最大值,其值从1开始,步长为1STDMETHOD(GetGroupCount)(/*[in]*/DISPID dispID, /*[out]*/long *nGroupCnt);//设置下拉列表控件中的预定义字符串(或true/false)STDMETHOD(GetPredefinedStrings)(/*[in]*/DISPID dispID, /*[out]*/CALPOLESTR *pCaStringsOut, /*[out]*/CADWORD *pCaCookiesOut);//设置下拉列表控件的当前显示字符串(或true/false)STDMETHOD(GetPredefinedValue)(/*[in]*/DISPID dispID, /*[in]*/DWORD dwCookie, /*[out]*/VARIANT *pVarOut);//设置列表分类/分组的名称STDMETHOD(GetCategoryName)(/* [in] */ PROPCAT propcat,/* [in] */ LCID lcid,/* [out] */ BSTR* pbstrName);//重置/覆盖列表中简单属性(单行显示)的名称STDMETHOD(GetDisplayName)(/*[in]*/DISPID dispID, /*[out]*/BSTR *pBstr);

15. 实现上述函数:

//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetElementValue (DISPID dispID, DWORD dwCookie, VARIANT *pVarOut) {if (pVarOut == NULL) return (E_POINTER);if (dispID == 1){int index;index = int(dwCookie / 3);/*dwCookie是一个计数器,是用于获取和设置每个属性项的值的唯一标识符,编号从0开始,依次递增1.本例中一个三维坐标对应3个连续的dwCookie值*/CComVariant var;get_Vertex(index, &var);AcAxPoint3d pt(var);pVarOut->vt = VT_R8;pVarOut->dblVal = pt[dwCookie % 3];return (S_OK);}else if (dispID == 3){CComVariant var;get_InsertPt(&var);AcAxPoint3d pt(var);pVarOut->vt = VT_R8;pVarOut->dblVal = pt[dwCookie];return (S_OK);}return (E_NOTIMPL) ;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::SetElementValue (DISPID dispID, DWORD dwCookie, VARIANT VarIn) {if (dispID == 1){int index;index = int(dwCookie / 3);CComVariant var;get_Vertex(index, &var);AcAxPoint3d pt(var);pt[dwCookie % 3] = VarIn.dblVal;pt.setVariant(var);put_Vertex(index, var);return (S_OK);}else if (dispID == 3){CComVariant var;get_InsertPt(&var);AcAxPoint3d pt(var);pt[dwCookie] = VarIn.dblVal;pt.setVariant(var);put_InsertPt(var);return (S_OK);}return (E_NOTIMPL) ;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetElementStrings (DISPID dispID, OPMLPOLESTR __RPC_FAR *pCaStringsOut, OPMDWORD __RPC_FAR *pCaCookiesOut) {if (pCaStringsOut == NULL || pCaCookiesOut == NULL)	return (E_POINTER);if (dispID == 1){pCaStringsOut->cElems = 3;pCaStringsOut->pElems = (LPOLESTR*)CoTaskMemAlloc(sizeof(LPOLESTR) * 3);pCaStringsOut->pElems[0] = SysAllocString(L"  顶点 x 坐标");pCaStringsOut->pElems[1] = SysAllocString(L"  顶点 y 坐标");pCaStringsOut->pElems[2] = SysAllocString(L"  顶点 z 坐标");pCaCookiesOut->cElems = 3;pCaCookiesOut->pElems = (DWORD*)CoTaskMemAlloc(sizeof(DWORD) * 3);pCaCookiesOut->pElems[0] = 0;pCaCookiesOut->pElems[1] = 1;pCaCookiesOut->pElems[2] = 2;return (S_OK);}else if (dispID == 3){pCaStringsOut->cElems = 3;pCaStringsOut->pElems = (LPOLESTR*)CoTaskMemAlloc(sizeof(LPOLESTR) * 3);pCaStringsOut->pElems[0] = SysAllocString(L"文本 x 坐标");pCaStringsOut->pElems[1] = SysAllocString(L"文本 y 坐标");pCaStringsOut->pElems[2] = SysAllocString(L"文本 z 坐标");pCaCookiesOut->cElems = 3;pCaCookiesOut->pElems = (DWORD*)CoTaskMemAlloc(sizeof(DWORD) * 3);pCaCookiesOut->pElems[0] = 0;pCaCookiesOut->pElems[1] = 1;pCaCookiesOut->pElems[2] = 2;return (S_OK);}return (E_NOTIMPL) ;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetElementGrouping (DISPID dispID, short *groupingNumber) {if (groupingNumber == NULL)	return (E_POINTER);if (dispID == 1){*groupingNumber = 3;//每组三个数据,及x、y、zreturn (S_OK);}return (E_NOTIMPL) ;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetGroupCount (DISPID dispID, long *nGroupCnt) {if (nGroupCnt == NULL)return (E_POINTER);if (dispID == 1){*nGroupCnt = 6; // 顶点数return S_OK;}return (E_NOTIMPL) ;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetPredefinedStrings(/*[in]*/DISPID dispID, /*[out]*/CALPOLESTR *pCaStringsOut, /*[out]*/CADWORD *pCaCookiesOut)
{if (dispID != 2)return  IOPMPropertyExtensionImpl<CMyObjectOPM>::GetPredefinedStrings(dispID, pCaStringsOut, pCaCookiesOut);USES_CONVERSION;if (dispID == 2){long size = 4;pCaStringsOut->pElems = (LPOLESTR *)::CoTaskMemAlloc(sizeof(LPOLESTR) * size);pCaCookiesOut->pElems = (DWORD *)::CoTaskMemAlloc(sizeof(DWORD) * size);pCaStringsOut->cElems = (ULONG)size;pCaCookiesOut->cElems = (ULONG)size;pCaStringsOut->pElems[0] = ::SysAllocString(_T("我们"));pCaCookiesOut->pElems[0] = 0;pCaStringsOut->pElems[1] = ::SysAllocString(_T("一起"));pCaCookiesOut->pElems[1] = 1;pCaStringsOut->pElems[2] = ::SysAllocString(_T("学习")); pCaCookiesOut->pElems[2] = 2;pCaStringsOut->pElems[3] = ::SysAllocString(_T("ObjectARX")); pCaCookiesOut->pElems[3] = 3;return S_OK;}return E_NOTIMPL;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetPredefinedValue(/*[in]*/DISPID dispID, /*[in]*/DWORD dwCookie, /*[out]*/VARIANT *pVarOut)
{if (dispID != 2)return  IOPMPropertyExtensionImpl<CMyObjectOPM>::GetPredefinedValue(dispID, dwCookie, pVarOut);USES_CONVERSION;const TCHAR* pName = NULL;if (dispID == 2){switch (dwCookie){case 0:pName = _T("我们");break;case 1:pName = _T("一起");break;case 2:pName = _T("学习");break;case 3:pName = _T("ObjectARX");break;default:return E_NOTIMPL;}::VariantCopy(pVarOut, &CComVariant(CT2W(pName)));return S_OK;}return E_NOTIMPL;
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetCategoryName(/* [in] */ PROPCAT propcat,/* [in] */ LCID lcid,/* [out] */ BSTR* pbstrName)
{if (propcat == WjcCategoryID1){*pbstrName = ::SysAllocString(_T("分类1 测试"));return S_OK;}else if (propcat == WjcCategoryID2){*pbstrName = ::SysAllocString(_T("分类2 测试")); return S_OK;}else{return S_FALSE;}
}//-----------------------------------------------------------------------------
STDMETHODIMP CMyObjectOPM::GetDisplayName(/*[in]*/DISPID dispID, /*[out]*/BSTR *pBstr)
{if (pBstr == NULL) return (E_POINTER);switch (dispID){case (0x401):*pBstr = ::SysAllocString(_T("对象特性表-乱石作品"));break;case (0x516):*pBstr = ::SysAllocString(_T("颜色"));break;case (0x501):*pBstr = ::SysAllocString(_T("图层"));break;case (0x502):*pBstr = ::SysAllocString(_T("线型"));break;case (0x503):*pBstr = ::SysAllocString(_T("线型比例"));break;case (0x513):*pBstr = ::SysAllocString(_T("打印样式"));break;case (0x514):*pBstr = ::SysAllocString(_T("线宽"));break;case (0x515):*pBstr = ::SysAllocString(_T("超链接"));break;case (0x577):*pBstr = ::SysAllocString(_T("材质"));break;case (0x579):*pBstr = ::SysAllocString(_T("实体透明度"));break;case (0x01):*pBstr = ::SysAllocString(_T("当前顶点"));break;case (0x02):*pBstr = ::SysAllocString(_T("格式文本"));break;case (0x04):*pBstr = ::SysAllocString(_T("说明"));break;default:break;}return (S_OK);
}

16. 重新编译后,在AutoCAD中的测试结果如图,此时选中自定义对象,特性表中已能显示出所需参数,且更改特性表中的参数,自定义对象亦能随之变化。成功!!!


http://www.ppmy.cn/news/706069.html

相关文章

cesium时间轴格式化为北京时间

中心思想&#xff1a;使用JulianDate.addHours()方法&#xff0c;将时间8. 效果&#xff1a; 初始配置&#xff1a;创建viewer时打开时间轴的配置&#xff08;默认打开&#xff09;&#xff0c;动画配置shouleAnimate为true。 // timeline: false, // animation: false, shou…

高压线路零序电流方向保护程序逻辑原理(三)

四、零序保护故障处理程序原理 零序保护逻辑程序可分为三个模块&#xff1a;快速动作部分、全相循环和非全相循环模块。这是根据零序保护的特点而设计的。任何一种高压线路保护都要求快速处理I段范围内的严重故障&#xff0c;所以零序保护与距离保护一样配有快速动作部分程序模…

一卡通管理系统需求分析

一卡通管理系统需求分析业务需求 1&#xff0e;业务目的&#xff1a;将校园的各类消费与身份认证功能集成在一卡通&#xff0c;实现“一卡在手&#xff0c;走遍校园”。为在校的师生和教学管理人员提供具有开放性的、灵活性的管理平台。 2&#xff0e;业务目标&#xff1a;实现…

0602 信用卡防盗刷学习总结

前言 国内出境游高企,为了方便游客,很多银行都推出了支持境外消费的信用卡。然而,近年来卡未离身却遭遇境外网上盗刷的案件呈高发态势,其原因在于国外信用卡网上消费的验证程序与国内不同,像亚马逊、eBay等国外电商,其网络消费仅需信用卡卡号及卡片背面的CVV2码(签名档…

如何实现多 Tab 同步登陆和退出

一. 场景再现 前两天接到一个需求&#xff0c;要求实现类似于 B站 的那种&#xff0c;当我同时打开多个 Tab 标签的时候&#xff0c;如果我在某一个窗口退出了&#xff0c;那么其它窗口的登陆状态也需要同步退出。如下图&#xff0c;我同时打开了两个 tab 。 当我点击其中一个…

基于jsp+Servlet+mysql的汽车销售系统

基于jspServletmysql的汽车销售系统 一、系统介绍二、功能展示1.项目骨架2.登录界面3.首页4.购物车5.添加车辆6、编辑车辆信息 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目类型&#xff1a;Java web项目 项目名称&#xff1a;基于JSPServlet的汽车销售系统 项目架…

iPhone导入电脑图片视频报错(系统没有发挥作用)

前言&#xff1a;把视频从iphone设备拷贝至PC时出现错误&#xff1a;连接到系统上的设备没有发挥作用 尝试了更换USB口和USB线后还是没有解决这个问题&#xff0c;后来发现是iPhone本身设置的原因。 解决办法&#xff1a;打开设置—点开图片—找到传输到MAC或PC的选项—改为保留…

iphone x计算机失灵,苹果x连接电脑没反应

语音内容&#xff1a; 大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。 苹果x连接电脑没反应的原因是&#xff1a; 1、连接前先确保数据线可以正常使用&#xff0c;并检查其与电脑、手机间的接口是否正常衔接。首先&#xff0c;请确…