一、块表总述
- 块表组成:
- 三个特殊的块表记录
- 名称:Model_Space、Paper_Space、Paper_Space0
- 特点:只有将实体或块参照插入以上三个块表中,才会显示
- 用户定义块表记录:块定义,不存在图层、颜色等属性,不显示
- 三个特殊的块表记录
二、块定义
- 功能:只是新增了一个块表记录,不显示,需转换成块参照插入三个特殊块表记录中才会显示
- 代码示例(见效果图的new)
#include "StdAfx.h" #include "Commands.h" #include "Editor.h" void AddCommands() { Editor::AddCommand(_T("AddBlock"), ACRX_CMD_MODAL, CreateBlock); } void CreateBlock() { // 创建一个空的块表对象指针pBlkTbl AcDbBlockTable *pBlkTbl = NULL; // 以写得方式打开数据库文件dwg,获得块表指针,并赋值给pBlkTbl acdbHostApplicationServices()->workingDatabase()-> getBlockTable(pBlkTbl, AcDb::kForWrite); // 创建一个块表记录对象(构造函数创建的)指针pBlkRcd AcDbBlockTableRecord *pBlkRcd = new AcDbBlockTableRecord(); // ACHAR:CAD中用于定义宽字符型变量 ACHAR blkName[40]; // 与用户交互:输入是否包含空格、提示文字(无则NULL)、 // 接收输入变量;RTNORM为return normal if (acedGetString(Adesk::kFalse, _T("\n 请输入块名(空格结束):"), blkName) != RTNORM) { // 未输入退出:关闭块表(针对dwg文件) pBlkTbl->close(); // 删除块表指针指向的内存(释放内存) delete pBlkRcd; // 结束退出 return; } // 设置块表记录名:跟Model_Space同级,见下图 pBlkRcd->setName(blkName); // 将块表记录添加进块表中 AcDbObjectId blkDefId; pBlkTbl->add(blkDefId, pBlkRcd); // 重要:添加后,调用类方法close关闭块表,可通过pBlkRcd继续操作块表记录 pBlkTbl->close(); // 创建两个数据库线对象指针 AcGePoint3d pt1(0, 0, 0), pt2(100, 0, 0), pt3(0, 100, 0); AcDbLine *pL_1 = new AcDbLine(pt1, pt2); AcDbLine *pL_2 = new AcDbLine(pt1, pt3); // 将对象指针添加进块表记录中,entId返回的是每次添加的对象返回的对象id AcDbObjectId entId; pBlkRcd->appendAcDbEntity(entId, pL_1); pBlkRcd->appendAcDbEntity(entId, pL_2); // 重要:添加后,调用close函数关闭直线对象 pL_1->close(); pL_2->close(); // 重要:调用类方法close关闭块表记录 pBlkRcd->close(); }
- 符号表查询结果
三、块参照类
3.1 封装类库
- 模板:以第(三)篇文件结构为基础,构建自己的类库
- 在AcDbEntity筛选器中添加
- Block.h
#pragma once class Block { public: Block(); ~Block(); // 插入块参照:块定义对象ID、插入点、比例、旋转弧度 static AcDbObjectId InsertBlkRef(AcDbObjectId blkDefId, const AcGePoint3d &ptInsert, double scale = 1.0, double rotation = 0.0); // 设置块参照比例:块参照指针、比例 static void SetBlkScale(AcDbBlockReference *pBlkRef, double scale = 1.0); // 获取块参照ID:传入块参照名称、数据库指针 static AcDbObjectId GetBlkDefId(const ACHAR *blkDefName, AcDbDatabase *pDb = acdbHostApplicationServices()-> workingDatabase()); };
- Block.cpp
#include "stdafx.h" #include "Block.h" #include "Database.h" Block::Block(){} Block::~Block(){} // 插入块参照:块定义对象ID、插入点、比例、旋转弧度 AcDbObjectId Block::InsertBlkRef(AcDbObjectId blkDefId, const AcGePoint3d &ptInsert, double scale , double rotation) { // 块参照对象指针:插入点,块定义对象ID AcDbBlockReference *pBlkRef = new AcDbBlockReference(ptInsert, blkDefId); // 设置块参照旋转弧度 pBlkRef->setRotation(rotation); // 设置块参照比例 SetBlkScale(pBlkRef, scale); // 添加进数据库文件dwg return Database::PostToModelSpace(pBlkRef); } // 设置块参照比例:块参照指针、比例 void Block::SetBlkScale(AcDbBlockReference *pBlkRef, double scale) { // 断言块参照指针:若为空则报错,判断括号里表达式真假 assert(pBlkRef); // 设置3d比例 AcGeScale3d geScale(scale, scale, scale); // 设置块参照比例:传入的需要是AcGeScale3d pBlkRef->setScaleFactors(geScale); } // 获取块定义ID:传入块定义名称、数据库指针 AcDbObjectId Block::GetBlkDefId(const ACHAR *blkDefName, AcDbDatabase *pDb) { // 块表指针 AcDbBlockTable *pBlkTbl = NULL; // 获得块表指针并给pBlkTbl赋值 Acad::ErrorStatus es = pDb->getBlockTable(pBlkTbl, AcDb::kForRead); // 块定义对象ID AcDbObjectId blkDefId; // 通过块定义名称获得块定义对象id es = pBlkTbl->getAt(blkDefName, blkDefId); // 关闭块表 pBlkTbl->close(); // 如果查找到,返回对象ID,没查找到返回空 if (es == Acad::eOk) { return blkDefId; } else { return AcDbObjectId::kNull; } }
3.2 测试代码
- Command.h
#include "StdAfx.h" void AddCommands(); void CreateBlock(); void InsertBlk();
- Command.cpp
#include "StdAfx.h" #include "Commands.h" #include "Editor.h" #include "MathUtil.h" #include "Block.h" void AddCommands() { // 块定义 Editor::AddCommand(_T("AddBlock"), ACRX_CMD_MODAL, CreateBlock); // 插入块参照 Editor::AddCommand(_T("InsertBlock"), ACRX_CMD_MODAL, InsertBlk); } void CreateBlock() { ...见块定义 } // 插入块参照 void InsertBlk() { // 块定义名称:这里需输入AddBlock命令增加的块定义的块名 ACHAR blkName[40]; if (acedGetString(Adesk::kFalse, _T("\n 请输入待插入的块名:"), blkName) != RTNORM) return; // 获得块定义ID:传入块定义名称 AcDbObjectId blkDefId = Block::GetBlkDefId(blkName); if (blkDefId.isNull()) { acutPrintf(_T("\n查无此块!")); return; } // 用户交互:选项、提示信息、插入点(后续解释) ads_point pt; if (acedGetPoint(NULL, _T("\n 选择插入点:"), pt) != RTNORM) return; // 将ads_point转换为AcGePoint3d AcGePoint3d pInsert = asPnt3d(pt); // 插入块参照:块定义ID、插入点、缩放比例、旋转弧度 Block::InsertBlkRef(blkDefId, pInsert, 2.0, MathUtil::PI() / 2); }
四、带属性的块参照类
- 目标效果
- 设计思路:
- 带属性的块参照:
4.1 封装类
- 模板:以第(三)篇文件结构为基础,构建自己的类库
- 在AcDbEntity筛选器中添加
- Block.h
// 插入带属性的块参照:块定义id、插入点、缩放比例、旋转弧度 static AcDbObjectId InsertBlkRefWithAtt(const AcDbObjectId &blkDefId, const AcGePoint3d &insertPoint, double scale = 1.0, double rotation = 0.0); // 添加属性定义到块参照:块参照指针、属性定义指针 static void appendAttributeToBlkRef(AcDbBlockReference *pBlkRef, AcDbAttributeDefinition *pAttDef); // 修改带属性的块参照中的属性:块参照指针、属性标签、Text对应的value新值 static bool SetBlockRefAttribute(AcDbBlockReference *pBlkRef, const ACHAR *tag, const ACHAR *val);
- Block.cpp
#include "StdAfx.h" #include "Block.h" #include "Database.h" #include "atlstr.h" // 需要添加这个,否则CString识别不出 Block::Block(){} Block::~Block(){} // 添加属性到块参照:块参照指针、属性定义指针 void Block::appendAttributeToBlkRef(AcDbBlockReference *pBlkRef, AcDbAttributeDefinition *pAttDef) { // 创建属性对象指针pAtt AcDbAttribute *pAtt = new AcDbAttribute(); // 从块参照获得属性对象的对象特性、图层 pAtt->setPropertiesFrom(pBlkRef); pAtt->setLayer(pBlkRef->layerId()); // 从属性定义获得可见性、定位、高度、宽度因子、旋转角度 pAtt->setInvisible(pAttDef->isInvisible()); pAtt->setPosition(pAttDef->position()); pAtt->setHeight(pAttDef->height()); pAtt->setWidthFactor(pAttDef->widthFactor()); pAtt->setRotation(pAttDef->rotation()); // 从属性定义获得水平模式、垂直模式、对齐点、文字样式、属性 pAtt->setHorizontalMode(pAttDef->horizontalMode()); pAtt->setVerticalMode(pAttDef->verticalMode()); pAtt->setAlignmentPoint(pAttDef->alignmentPoint()); pAtt->setTextStyle(pAttDef->textStyle()); pAtt->setAttributeFromBlock(pBlkRef->blockTransform()); // 从属性定义中 获得 并设置 标签tag名,就是样例为“直径” ACHAR *pStr = pAttDef->tag(); pAtt->setTag(pStr); // 删除字符串指针 acutDelString(pStr); // 从属性定义中获得命名空间长度、设置text的value pAtt->setFieldLength(pAttDef->fieldLength()); pAtt->setTextString(pAttDef->textString()); // 将属性对象添加进块参照中,构成属性块参照 pBlkRef->appendAttribute(pAtt); // 关闭属性对象 pAtt->close(); } // 插入带属性的块参照:块定义id、插入点、缩放比例、旋转弧度 AcDbObjectId Block::InsertBlkRefWithAtt(const AcDbObjectId &blkDefId, const AcGePoint3d &insertPoint, double scale, double rotation) { // 创建块参照对象指针:插入点、块定义id, // 带属性的块参照只是在块参照上增加了个文字属性,还是块参照 AcDbBlockReference *pBlkRef = new AcDbBlockReference(insertPoint, blkDefId); // 设置块参照比例:参见本文件 SetBlkScale(pBlkRef, scale); // 给块参照设置旋转弧度 pBlkRef->setRotation(rotation); // 查找块定义所在的块表记录 AcDbBlockTableRecord *pBlkTblRcd = NULL; if (acdbOpenObject(pBlkTblRcd, blkDefId, AcDb::kForRead) == Acad::eOk) { // 如果块表记录含有属性定义对象 if (pBlkTblRcd->hasAttributeDefinitions()) { // 创建块表记录迭代器pItr AcDbBlockTableRecordIterator *pItr = NULL; // 初始化pItr pBlkTblRcd->newIterator(pItr); // 遍历块表记录中的每个实体对象,为了找属性定义对象 for (pItr->start();!pItr->done(); pItr->step()) { // AcDbEntity是所有实体的基类 AcDbEntity *pEnt = NULL; // 查找属性定义:打开每个实体 if (pItr->getEntity(pEnt, AcDb::kForRead) == Acad::eOk) { // 如果实体是属性定义的派生类 if (pEnt->isKindOf(AcDbAttributeDefinition::desc())) { // 强制类型转换,转成属性定义的指针pAttdef AcDbAttributeDefinition *pAttdef = AcDbAttributeDefinition::cast(pEnt); // 调用本文件的函数,将属性定义加工后添加进块参照中 appendAttributeToBlkRef(pBlkRef, pAttdef); } }// 循环内,每次打开的实体对象都要关闭 pEnt->close(); }// 循环结束,删除迭代器 delete pItr; } // 关闭块表记录 pBlkTblRcd->close(); }// 将块参照(属性块参照)添加进数据库文件dwg中 return Database::PostToModelSpace(pBlkRef); } // 修改带属性的块参照的属性:块参照指针、tag标签名、text的value新值 bool Block::SetBlockRefAttribute(AcDbBlockReference *pBlkRef, const ACHAR *tag, const ACHAR *val) { AcDbBlockTableRecord *pBlkTblRcd = NULL; bool bRet = true; // 提取 块参照 的 块表记录指针pBlkTblRcd: // 块参照指针->blockTableRecord() // 返回 块参照 的 块表记录 的 对象id if (acdbOpenObject(pBlkTblRcd, pBlkRef->blockTableRecord(), AcDb::kForRead) == Acad::eOk) { // 判断模型空间中的块表记录(块参照所在的)是否含有属性 if (pBlkTblRcd->hasAttributeDefinitions()) { // 创建 块表记录迭代器 指针 及 初始化 AcDbBlockTableRecordIterator *pItr = NULL; pBlkTblRcd->newIterator(pItr); AcDbEntity *pEnt = NULL; // 通过 块表记录迭代器 遍历 块表记录中 各个实体 找 属性对象 for (pItr->start();!pItr->done(); pItr->step()) { pItr->getEntity(pEnt, AcDb::kForRead); // 如果找到属性对象:见代码下的解释 if (pEnt->isKindOf(AcDbAttributeDefinition::desc())) { // 强制类型转换为 属性定义指针(从基类到派生类) AcDbAttributeDefinition *pAttDef = AcDbAttributeDefinition::cast(pEnt); // 获得属性定义的tag标签名:此例为“直径” ACHAR *pszTag = pAttDef->tag(); // 浅拷贝个副本 CString strTag = pszTag; // 释放pszTag指针内存,未删除指针 acutDelString(pszTag); // 找到目标标签tag:不区分大小写的比较strTag和tag(相同为0) if (strTag.CompareNoCase(tag) == 0) { // 是否发现目标tag标签 bool bFound = false; // 创建块参照 的 属性迭代器(一个属性块可以有多个属性) // 方法的返回值为AcDbObjectIterator * AcDbObjectIterator *attIt = pBlkRef->attributeIterator(); // 循环迭代读取每个属性 for (attIt->start();!attIt->done();attIt->step()) { AcDbAttribute *pAtt = NULL; // attIt->objectId():属性遍历器获得对象ID的方法, // 属性对象指针pAtt接收返回值 Acad::ErrorStatus es = acdbOpenObject(pAtt, attIt->objectId(), AcDb::kForWrite); if (es == Acad::eOk) { // 提取属性对象的标签,此例“直径” pszTag = pAtt->tag(); strTag = pszTag; acutDelString(pszTag); // 当当前遍历的标签名strTag和传入的目标标签名tag匹配 if (strTag.CompareNoCase(tag) == 0) { // 修改属性的text的value值 pAtt->setTextString(val); bFound = true; }// 关闭属性对象 pAtt->close(); } }// 删除属性迭代器 delete attIt; // 如果没发现匹配的属性标签tag,将属性定义添加进块参照中 if (!bFound) { // 将属性添加进块参照中 appendAttributeToBlkRef(pBlkRef, pAttDef); } } }// 关闭实体 pEnt->close(); }// 删除实体迭代器 delete pItr; }// 提取失败:返回false else { // 修改失败 bRet = false; }// 关闭块表记录 pBlkTblRcd->close(); }// 返回是否修改是否成功 return bRet; }
- 带属性的块参照在炸开后,属性会自动转换为属性定义对象
- 带属性的块参照在炸开后,属性会自动转换为属性定义对象
4.2 测试代码
- Command.h
#include "StdAfx.h" void AddCommands(); void CreateAttBlk(); void InsertAtt();
- Command.cpp
#include "StdAfx.h" #include "Commands.h" #include "Editor.h" #include "MathUtil.h" #include "Block.h" #include "atlstr.h" // 需要这个,否则CString无法识别 void AddCommands() { // 属性块定义 Editor::AddCommand(_T("AddAttBlk"), ACRX_CMD_MODAL, CreateAttBlk); // 插入属性块参照 Editor::AddCommand(_T("InsertAtt"), ACRX_CMD_MODAL, InsertAtt); } // 创建属性块定义 void CreateAttBlk() { // 以下为创建块定义代码,同上一节 AcDbBlockTable *pBlkTbl = NULL; acdbHostApplicationServices()->workingDatabase()->getBlockTable(pBlkTbl, AcDb::kForWrite); AcDbBlockTableRecord *pBlkRcd = new AcDbBlockTableRecord(); ACHAR blkName[40]; if(acedGetString(Adesk::kFalse,_T("\n 请输入块名(空格结束):"),blkName) != RTNORM) { pBlkTbl->close(); delete pBlkRcd; return; } pBlkRcd->setName(blkName); AcDbObjectId blkDefId; pBlkTbl->add(blkDefId, pBlkRcd); pBlkTbl->close(); AcGePoint3d pt1(0, 0, 0), pt2(100, 0, 0), pt3(0, 100, 0); AcDbLine *pL_1 = new AcDbLine(pt1, pt2); AcDbLine *pL_2 = new AcDbLine(pt1, pt3); // 创建属性定义对象指针:插入点、tag标签、text的value值、提示信息 AcDbAttributeDefinition *pAttDef = new AcDbAttributeDefinition(pt3, _T("20"), _T("直径"), _T("请输入直径")); // 将对象指针添加进块表记录中,entId返回的是每次添加的对象返回的对象id AcDbObjectId entId; pBlkRcd->appendAcDbEntity(entId, pL_1); pBlkRcd->appendAcDbEntity(entId, pL_2); // 将属性定义添加进块定义中 pBlkRcd->appendAcDbEntity(entId, pAttDef); pL_1->close(); pL_2->close(); pAttDef->close(); pBlkRcd->close(); } // 插入带属性的块参照 void InsertAtt() { ACHAR blkName[40]; if (acedGetString(Adesk::kFalse, _T("\n请输入带属性的块参照名:"), blkName) != RTNORM) { return; } // 调用函数获得 块定义的对象id AcDbObjectId blkDefId = Block::GetBlkDefId(blkName); if (blkDefId.isNull()) { acutPrintf(_T("查无此属性块定义")); return; } ads_point pt; if (acedGetPoint(NULL, _T("\n请选择插入点:"), pt) != RTNORM) { return; } AcGePoint3d pInsert = asPnt3d(pt); // 插入带属性的块参照:块定义对象id、插入点、比例、旋转角度 AcDbObjectId blkRefId = Block::InsertBlkRefWithAtt(blkDefId, pInsert, 2, MathUtil::PI()/2); AcDbBlockReference *pBlkRef = NULL; if (acdbOpenObject(pBlkRef, blkRefId, AcDb::kForWrite) == Acad::eOk) { // 调用函数 修改块参照 的 属性值:块参照、标签名、text的value新值 Block::SetBlockRefAttribute(pBlkRef, _T("直径"), _T("600")); pBlkRef->close(); } }
4.3 符号表查询结果
-
带属性的块参照123(命令AddAttBlk)
注意:块定义(包含属性定义)都是跟模型空间块平级的块表记录
-
块定义中的实体对象
注意:块定义中实体是被并列的堆放在块定义中的,所以需要遍历
-
模型空间中的带属性的块参照
属性:是从块定义中提取属性定义并添加进块参照中,属性对象常依附于块参照
重点值:tag:“直径”、Text:20