目录
一、概述
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方法)