objectArx ---反应器
目录
- 一、概述
- 1.1 概念
- 1.2 种类
- 二、数据库反应器
- 2.1 反应器创建
- 2.2 类文件修改
- 2.3 注册卸载反应器
- 三、编辑反应器
- 3.1 反应器创建
- 3.2 类文件修改
- 3.3 注册卸载反应器
- 四、配置文件管理器反应器
- 4.1 反应器创建
- 4.2 类文件修改
- 4.3 测试文件
- 4.4 效果
- 五、上下文反应器
- 5.1 反应器创建
- 5.2 类文件修改
- 5.3 注册卸载反应器
- 5.4 效果
一、概述
1.1 概念
- 定义:反应器机制是观察者模式(设计模式)的一种实现,在该机制下,有事件通知者和事件接收者,负责接收事件的称为反应器
- 反应器列表:在反应器可以从通知者处接收消息之前,必须显式地将反应器添加到通知者的反应器列表中(观察者模式中的通知对象列表)。
- 反应器部分类继承关系
1.2 种类
- 常用反应器种类
类型 派生 示例 数据库反应器 派生于AcDbDatabaseReactor,负责接收与数据库状态相关的事件 对象被加入、删除、修改到数据库中,这种反应器的通知发送者是数据库,此种反应器被加入到数据库的反应器列表中 对象反应器(自定义类中详述) 派生于AcDbObjectReactor,
负责接收对象(object)级别的事件复制、删除、修改一个对象,它也可以被加入到任何AcDbObject对象的反应器列表中 编辑反应器 派生于AcEditorReactor,
负责接收AutoCAD的特殊事件例如加载或卸载一张图,开始或结束一个命令以及其他类型的用户交互。AcEditor对象是AcEditorReactor反应器的唯一通知发送者 配置文件管理器反应器 派生于AcApProfileManagerReactor,负责接收配置文件操作的事件 例如对配置文件的增删改查 输入上下文反应器 派生于AcEdInputContextReactor,负责接收用户输入动作的事件 例如:用户在屏幕上点选、获取角度、获取距离等
- 反应器使用原则
- 不要依赖反应器激活的顺序
- 不要依赖通知间操作的顺序:通知只负责告知有无,不负责先后
- 不要在通知回调函数中使用任何用户交互函数
- 临时反应器
- 使用过程:从ARX内建的一系列反应器类中挑选一个合适的类派生,实现相关函数并注册反应器。
- 特点:非数据库对象,由开发者负责注册、卸载
- 包含:数据库反应器、编辑反应器
- 永久反应器
- 特点:永久反应器由AutoCAD负责删除
- 包含:对象反应器
二、数据库反应器
2.1 反应器创建
- 向导创建
2.2 类文件修改
- CDatabaseReactor.h
class /*DLLIMPEXP*/ CDatabaseReactor : public AcDbDatabaseReactor {... public:... // 重写基类AcDbDatabaseReactor 相应方法virtual void objectAppended(const AcDbDatabase* dwg, const AcDbObject* dbObj);virtual void objectModified(const AcDbDatabase* dwg, const AcDbObject* dbObj);virtual void objectErased(const AcDbDatabase* dwg, const AcDbObject* dbObj,Adesk::Boolean bErased); } ; ...
- CDatabaseReactor.cpp
#include "StdAfx.h" #include "CDatabaseReactor.h"// 定义全局变量 class CDatabaseReactor; CDatabaseReactor *pDRec = NULL;... void printObj(const AcDbObject* pObj) { // 纠错:防止传入空对象if (pObj == NULL){acutPrintf(_T("(NULL)"));return;}// 句柄:用于获取对象句柄对象AcDbHandle objHand;// 句柄名称:用于获取对象句柄名称字符串ACHAR handbuf[128];// 获取 句柄对象 的方法pObj->getAcDbHandle(objHand);// 获取 对象句柄 名称字符串 方法:通过句柄对象objHand.getIntoAsciiBuffer(handbuf, 128);// s表示字符串,l表示数据为长整型,x表示输出十六进制acutPrintf(_T("\n (类名:%s, 句柄:%s, 对象id:%lx)"), // 类名pObj->isA()->name(), // 句柄对象 名称handbuf, // asOldId方法:将对象id转换为长整型 long 格式pObj->objectId().asOldId()); }// 新增实体后,反应器进行的操作 void CDatabaseReactor::objectAppended(const AcDbDatabase* dwg, const AcDbObject* dbObj) {acutPrintf(_T("\n调用反应器 对象新增 方法"));printObj(dbObj); }// 修改实体后,反应器进行的操作 void CDatabaseReactor::objectModified(const AcDbDatabase* dwg, const AcDbObject* dbObj) {acutPrintf(_T("\n调用反应器 对象修改 方法"));printObj(dbObj); }// 删除实体后,反应器进行的操作 void CDatabaseReactor::objectErased(const AcDbDatabase* dwg, const AcDbObject* dbObj, Adesk::Boolean bErased) {if (bErased){acutPrintf(_T("\n调用反应器 对象删除 方法"));printObj(dbObj);}else{acutPrintf(_T("\n调用反应器 对象删除 方法(恢复删除)"));printObj(dbObj);} }
2.3 注册卸载反应器
- Commands.h
#include "stdafx.h"void AddCommands(); void addReactor(); void removeReactor();
- Commands.cpp
#include "stdafx.h" #include "Commands.h" #include "Editor.h" #include "CDatabaseReactor.h" // 声明全局变量pDRec:数据库反应器指针 extern CDatabaseReactor *pDRec;void AddCommands() { Editor::AddCommand(L"c-addReactor", ACRX_CMD_MODAL, addReactor);Editor::AddCommand(L"c-removeReactor", ACRX_CMD_MODAL, removeReactor); }void addReactor() {if (pDRec == NULL){ pDRec = new CDatabaseReactor();}// 在当前活动数据库中添加 数据库反应器acdbHostApplicationServices()->workingDatabase()->addReactor(pDRec);acutPrintf(_T("\n反应器已经添加!")); }void removeReactor() {if (pDRec){// 在当前活动数据库中删除 数据库反应器acdbHostApplicationServices()->workingDatabase()->removeReactor(pDRec);// 释放内存delete pDRec;// 重置指针pDRec = NULL;}acutPrintf(_T("\n数据库反应器已经删除!")); }
遇到问题:拉伸操作,CAD也会调用反应器删除、修改函数
三、编辑反应器
3.1 反应器创建
- 向导创建
3.2 类文件修改
-
CEditorreactor.h
class CEditorreactor : public AcEditorReactor {... public:...virtual void commandWillStart(const ACHAR* cmdStr);virtual void commandEnded(const ACHAR* cmdStr); } ;
-
CEditorreactor.cpp
#include "StdAfx.h" #include "CEditorreactor.h" // 添加一个全局变量 CEditorreactor *pERec = NULL; ... // 重写命令执行前操作 void CEditorreactor::commandWillStart(const ACHAR* cmdStr) {acutPrintf(_T("\n命令【%s】开始执行"), cmdStr); }// 命令执行后操作 void CEditorreactor::commandEnded(const ACHAR* cmdStr) {acutPrintf(_T("\n命令【%s】执行完毕"), cmdStr); }
3.3 注册卸载反应器
- Commands.h
#include "stdafx.h"void AddCommands(); void AttacthEditor(); void RemoveEditor();
- Commands.cpp
#include "stdafx.h" #include "Commands.h" #include "Editor.h" #include "CEditorreactor.h"extern CEditorreactor *pERec;void AddCommands() { Editor::AddCommand(L"c-addReactor", ACRX_CMD_MODAL, AttacthEditor);Editor::AddCommand(L"c-removeReactor", ACRX_CMD_MODAL, RemoveEditor); }void AttacthEditor() {if (pERec == NULL){pERec = new CEditorreactor(); }// 添加编辑反应器方法pERec->Attach(); }void RemoveEditor() {if (pERec){ // 卸载编辑反应器方法pERec->Detach();// 释放CEditorreactor对象内存delete pERec;// 重置指针pERec = NULL;} }
四、配置文件管理器反应器
4.1 反应器创建
- 向导创建
4.2 类文件修改
-
CProfileManReactor.h
class CProfileManReactor : public AcApProfileManagerReactor {... public:... virtual void currentProfileWillChange(const ACHAR *newProfile);virtual void currentProfileChanged(const ACHAR *newProfile); } ;
-
CProfileManReactor.cpp
... void CProfileManReactor::currentProfileWillChange(const ACHAR *newProfile) {acutPrintf(_T("\n/*******当前配置文件将要改变:%s*******/"), newProfile); }void CProfileManReactor::currentProfileChanged(const ACHAR *newProfile) {acutPrintf(_T("\n/*******当前配置文件已经被改变:%s*******/"), newProfile); }
4.3 测试文件
- 注:包含配置文件操作、添加删除反应器语句
- Commands.h
#include "stdafx.h"void AddCommands(); void ProfileManReactor();
- Commands.cpp
#include "stdafx.h" #include "Commands.h" #include "Editor.h" #include "CProfileManReactor.h"void AddCommands() { Editor::AddCommand(L"c-addReactor", ACRX_CMD_MODAL, ProfileManReactor); }// 配置文件管理器 及 反应器 void ProfileManReactor() { // 新建 配置文件管理器反应器 对象CProfileManReactor *pProfileReactor = new CProfileManReactor();// acProfileManagerPtr:通过此函数进入CAD配置文件管理器// addReactor:添加反应器来接收 配置文件管理器 通知acProfileManagerPtr()->addReactor(pProfileReactor);/****** 以下为配置文件操作 ******/// 获得配置文件在 注册表 中的注册路径 字符串(注意释放)ACHAR *pstrKey;// 参数:返回路径字符串,传入配置文件名(null为第一个)acProfileManagerPtr()->ProfileRegistryKey(pstrKey, NULL);if (pstrKey != NULL){acutPrintf(_T("\n配置文件的注册关键字为: %s"), pstrKey);acutDelString(pstrKey);}// ProfileListNames函数:接收 配置文件名称 字符串列表(查看函数声明,即可知道参数类型)// nProfiles:为函数 返回 配置文件 个数AcApProfileNameArray arrNameList;int nProfiles = acProfileManagerPtr()->ProfileListNames(arrNameList);acutPrintf(_T("\n当前用户配置文件的个数为:%d"), nProfiles);// 遍历列表方法for (int i = 0; i < arrNameList.length(); i++)acutPrintf(_T("\n配置文件名称为: %s"), arrNameList.at(i));// 从已有配置文件拷贝创建新的:新文件名、拷贝源文件名、新文件注释文字acProfileManagerPtr()->ProfileCopy(_T("TestProfile"), _T("<<未命名配置>>"), _T("注释1"));// 恢复默认设置:此名称下的配置文件会被重设为系统默认值acProfileManagerPtr()->ProfileReset(_T("TestProfile"));// 应用为当前配置:配置文件名acProfileManagerPtr()->ProfileSetCurrent(_T("TestProfile"));// 通过acedSetVar修改全局变量(此时修改的是TestProfile配置文件的)struct resbuf rbCursorSize;rbCursorSize.restype = RTSHORT;rbCursorSize.resval.rint = 100;// 修改鼠标十字光标的百分比(可以在CAD命令行输入CURSORSIZE看下效果)acedSetVar(_T("CURSORSIZE"), &rbCursorSize);// 重命名配置文件:新名、原名、新文件注释文字acProfileManagerPtr()->ProfileRename(_T("TestProfile2"), _T("TestProfile"), _T("注释2"));// 导出配置文件(.arg格式):待导出配置文件名、导出文件路径(REGEDIT4 格式)acProfileManagerPtr()->ProfileExport(_T("TestProfile2"), _T("C:/TestProfile2.arg"));/* 导入配置文件(.arg格式):新建配置文件名,导入文件路径(REGEDIT4 格式)、新文件注释文字、是否读入路径信息*/acProfileManagerPtr()->ProfileImport(_T("TestProfile3"),_T("C:/TestProfile2.arg"),_T("复制的配置文件"), Adesk::kTrue);/****** 配置文件操作结束 ******/// 删除反应器acProfileManagerPtr()->removeReactor(pProfileReactor); }
注意:除非为非当前正在应用配置, ProfileImport默认覆盖同名配置文件
4.4 效果
- 图示
五、上下文反应器
5.1 反应器创建
- 向导创建
5.2 类文件修改
- CInputContextReactor.h
... class CInputContextReactor : public AcEdInputContextReactor { ... public:...// 开始静止状态virtual void beginQuiescentState();// 结束静止状态virtual void endQuiescentState(); } ;
- CInputContextReactor.cpp
... // 定义全局变量 class CInputContextReactor; CInputContextReactor *pIRec = NULL; ... // 开始静止状态 void CInputContextReactor::beginQuiescentState() {acutPrintf(_T("\n开始静止状态!")); } // 结束静止状态 void CInputContextReactor::endQuiescentState() {acutPrintf(_T("\n结束静止状态!")); }
5.3 注册卸载反应器
- Commands.h
#include "stdafx.h"void AddCommands(); void addReactor(); void removeReactor();
- Commands.cpp
#include "stdafx.h" #include "Commands.h" #include "Editor.h" #include "Database.h" #include "CInputContextReactor.h"extern CInputContextReactor *pIRec;void AddCommands() { Editor::AddCommand(L"c-addReactor", ACRX_CMD_MODAL, addReactor);Editor::AddCommand(L"c-removeReactor", ACRX_CMD_MODAL, removeReactor); }void addReactor() { if (pIRec == NULL){pIRec = new CInputContextReactor();}curDoc()->inputPointManager()->addInputContextReactor(pIRec);acutPrintf(_T("\n上下文反应器已经添加!")); }void removeReactor() {if (pIRec){curDoc()->inputPointManager()->removeInputContextReactor(pIRec);delete pIRec;pIRec = NULL;}acutPrintf(_T("\n上下文反应器已经删除!")); }
5.4 效果
- 图示
- 改写示意(基类AcEdInputContextReactor方法)
传送门 返回 列表