leeyeafu 发表于 2003-5-27 13:03:00

[连载]ObjectARX程序设计入门(1)

先说几句,我与明经通道相识几个月来,帖子发了不少,于是又想过把作者瘾,在明经文章中心发几篇文章。真的动起手来却发现文章确实不好写。
ObjectARX For ACAD R14至ACAD2002使用VC6作为开发语言,而For ACAD2004应该使用VC.NET进行程序设计。以下的文章基本上以ARX For ACAD2002为主,兼顾2004即VC.NET程序设计方法,对于其他ACAD版本下的ARX程序设计,基本方法是相同的,仅有某些细节上的区别。
我个人对ARX程序设计理解本来就不够深刻,再加上文字水平不高,文章中错误必然很多。因此希望各位网友多多批评指教。当大多数网友满意后再在明经的文章中心张帖。在此,我不想抄书,也不想翻译ARX帮助文档(当然,完全不用书本和帮助文档是不现实的),只想将一些个人理解完整地写出来。另外,除书本和帮助文档,*.h头文件和ARX提供的示例代码也是学习ARX程序设计的好帮手。
哆嗦了许多,以下是正文。
ObjectARX程序设计入门--1、图形数据库操作
AutoCAD图形实际上是一系列存放在一个AcDbDatabase类型的数据库中的AcDb对象。AcDbDatabase数据库中所有的对象都有一个句柄,在一个图形(DWG)文件中,对象句柄是唯一的,用来标识对象。AutoCAD图形实体(AcDbEntity类对象)只是AcDbDatabase数据库的一种特殊的对象,用户可以在图形窗口看到并编辑它。AcDbDatabase数据库中的对象还有符号表、词典和其它与AcDb类对象相关的符号。数据库中所有的图形实体和对象都可以使用ObjectARX技术,通过程序设计来进行修改和编辑。这一点其实也就是AutoCAD二次开发的基本出发点和最终目标。<这话对吗?总感觉有些不妥,但不知道如何表达才准确。><尖括号中的内容是我个人感想,不会出现在最终文档中。>
AcDbDatabase既然是数据库,它必然具备数据库的基本组织结构。它由多个表(Table)和表中的记录(Record)构成。AcDbDatabase数据库包括九个符号表和一个命名对象词典。<下面这张图表与大部分教材不同,各位以为如何?>
AcDbDatabase数据库
       |------AcDb符号表
       |      |------块表(AcDbBlockTable)
       |      |------尺寸标注样式表(AcDbDimStyleTable)
       |      |------层表(AcDbLayerTable)
       |      |------线型表(AcDbLinetypeTable)
       |      |------已注册应用程序表(AcDbRegAppTable)
       |      |------字体样式表(AcDbTextStyleTable)
       |      |------用户坐标系表(AcDbUCSTable)
       |      |------视口表(AcDbViewportTable)
       |      |------视图表(AcDbViewTable)
       |-------命名对象词典
AcDbDatabase数据库使用了面向对象的数据库(OODB)技术,用传统的数据库的术语来说,它甚至不是第一范式(1NF)的,记录的组织形式与普通的数据库有较大的不同。<这大概是很多人没把AutoCAD图形看作数据库的原因。>在AutoCAD命令行运行LISP函数entget可以清楚地看到实体在AcDbBlockTable块表中的记录组织形式。当然,在ObjectARX技术中,关于数据库记录的细节进行了数据封装,对数据库的表和记录进行操作要通过访问各个类的成员函数来实现。
以下详细解释AcDbDatabase数据库操作的ObjectARX技术。
1.1 新建数据库、使用已有的数据库
要使用AcDbDatabase数据库,与使用其它数据库一样,首先要声明一个数据库对象。AcDbDatabase类的声明,调用类的构造函数:
AcDbDatabase ::AcDbDatabase(bool buildDefaultDrawing = true, bool noDocument = false);
其中,buildDefaultDrawing参数控制是否创建一个包括所有默认数据记录的数据库。该参数的默认值为true,这样在AcDbDatabase数据库创建时,将在新建的数据库中包含一个AutoCAD图形数据库必须包含的基本数据要素,这些要素包括九个符号表及其初始记录(比如0层、STANDARD文字样式等),命名对象词典(组词典和多线样式词典)以及必要的系统变量设置。这样就可以向新建的数据库中添加各种实体和对象。若指定该参数为false,AutoCAD将创建一个完全空的AcDbDatabase数据库。这样的AcDbDatabase数据库不能直接向其中添加实体或对象,需读入一个图形文件来拓展该图形数据库,然后再逐步添加或修改数据库中的实体或对象。读入图形数据库使用函数:
AcadErrorStatus AcDbDatabase::readDwgFile(const char* fileName );
<其实,readDwgFile()函数还有两个参数,分别控制数据库读写权限和内码转换,好象在这没必要作更深入的讨论,因此略过。>
AcDbDatabase构造函数的noDocument参数控制打开或的数据库是否与当前文档关联。此参数的使用将在“多文档处理”一节中详细讨论。
这样,要使用AcDbDatabase数据库,若准备新建数据库并向其中添加实体或对象,使用这样的代码:
AcDbDatabase *pDb= new AcDbDatabase();
若要使用已有的图形,比如“test.dwg”文件,则使用下列代码:
AcDbDatabase *pDb= new AcDbDatabase(false);
pDb->readDwgFile(“test.dwg”);
注意,在这种情况下,AcDbdatabase()的参数必须指定为false(当然,0或者Adesk::kFalse都可以)。<ARX帮助中说必须为Adesk::kFalse,AutoDesk是不是有点老大作风?>因为使用pDb->readDwgFile()函数时,pDb必须是一个完全空白的数据库,也就是说,哪怕使用了AcDbDatabase(false);新建数据库,只要对数据库进行了操作,就不能再使用readDwgFile()函数,否则会导致严重的内存错误。
还有一种情况,ARX程序只对当前图形数据库进行操作,在ACAD R14及以前版本和相应的ARX中使用:
AcDbDatabase *pDb=acdbCurDwg();//该函数返回指向当前图形数据库的指针
在ACAD 2000以上版本中也可以使用相同的方法,不过建议使用下列函数:
AcDbDatabase *pDb=acdbHostApplicationServices()->workingDatabase();
实际上,在ARX 2000及以上版本中,acdbCurDwg()已经定义为宏,宏扩展后就是上述函数。
1.2 使用数据库表和记录
新建或打开图形数据库后,可获取表和记录的指针,然后再对记录进行操作。要获取AcDbDatabase数据库记录指针,大致分为两个步骤:
首先,获取数据库表的指针。为了获取该指针,须声明一个类指针,该指针类型决定要使用的数据库表的类型(或名称)。比如说,要使用AcDb的块表及其记录,应该这样声明指针类:AcDbBlockTable *pBlkTbl;
然后使用AcDbDatabase类的成员函数获取表指针。获取块表指针可使用函数getBlockTable()函数。
Acad::ErrorStatus AcDbDatabase::getBlockTable(AcDbBlockTable*& pBlkTbl,
AcDb::OpenMode mode);
该函数的第一个参数是一个指向表引用(或者说地址)的指针。用于函数输出块表指针。另一个参数控制使用表的读写权限。
其它符号表指针的获取与块表指针的获取基本一致,可参考ARX帮助或dbsymtb.h头文件。从ARX2000开始,在AcDbDatabase类的成员函数中,还有一种方法可获取数据库中符号表的指针。即使用getSymbolTable()函数,该函数有九个重载版本,分别对应九个符号表,几个重载版本之间仅第一个参数的类型不同。例如,要新建图层,可以这样获取层表指针:
AcDbLayerTable* pLayerTbl;
pDb->getSymbolTable(pLayerTbl,AcDb::kForWrite);//此处pDb指已打开的数据库指针。
使用数据库,最终往往要对数据库表中的记录进行操作,因此打开表后,就需要获取记录指针。所有AcDb符号表记录指针都可通过调用对应表的getAt()函数来实现。每一个getAt()函数都有两个重载版本,分别输出记录ID或记录指针。<是不是太哆嗦?>
getAt()函数完整语法:
输出记录指针:
Acad::ErrorStatus AcDb##BASE_NAME##Table::getAt(const char* pEntryName,
AcDb##BASE_NAME##TableRecord*&pRecord, AcDb::OpenMode mode,
Adesk::Boolean openErasedRecord =Adesk::kFalse) const;
输出记录ID:
Acad::ErrorStatus AcDb##BASE_NAME##Table::getAt(const char* pEntryName,
AcDbObjectId& recordId, Adesk::Boolean getErasedRecord =Adesk::kFalse) const;
实际应用时,将##BASE_NAME##替换成九个符号表的实际类型。
在这解释两个参数,char* pEntryName要求输入符号表记录名称,例如块表中至少有两条记录,模型空间(可用宏ACDB_MODEL_SPACE访问记录名称)和图纸空间(即ACDB_PAPER_SPACE)。
最后,取出数据库符号表记录后,若不再使用表对象,应及时关闭,回收系统资源。
AcDb##BASE_NAME##Table::close();
1.3 添加、删除记录
获取数据库记录指针或记录ID后,就可以对数据库记录进行操作了,这包括在数据库中添加、删除记录,提取记录中的数据,即查找记录。
向数据库中添加记录,通用的方法是调用符号表类的add()成员函数。
Acad::ErrorStatus AcDb##BASE_NAME##Table ::add(AcDbSymbolTableRecord* pRecord); 或
Acad::ErrorStatus AcDb##BASE_NAME##Table ::add(AcDbObjectId& recordId,
AcDbSymbolTableRecord* pRecord);//该函数第一个参数用于输出新添加的记录ID
在这要说明一下,通常我们向模型空间添加实体不用这些方法。因为模型空间本身就是AcDb块表中的一条记录,向其中添加实体仅仅是对该记录进行操作,不需要向数据库中添加记录。
1.4 图形数据库实例
第一个例子来自ARX示例文档,目录\ObjectARX xxxx\docsamps\ents\ents.cpp文件演示了打开当前图形数据库并添加实体对象以及层表和组词典的使用方法。这个例子首先在当前图形数据库中建立一个线对象和一个圆对象,将圆的颜色变为红色,并与线对象组合成组(Group),另外,还在当前图形数据库中添加一个新图层。
下一个例子是对上一例子中添加图层的方法做出完善。在这个例子中将说明对层表和线型表及其记录操作的方法。
void addLayer()
{AcDbLayerTable *pLayerTbl;//先声明一个空的层表指针
   acdbHostApplicationServices()->workingDatabase()//当前图形数据库
       ->getSymbolTable(pLayerTbl, AcDb::kForWrite);//打开层表为写入状态
   if (!pLayerTbl->has("MyLayer")) {   //has()函数是层表类的成员函数
      AcDbLayerTableRecord *pLayerTblRcd= new AcDbLayerTableRecord;//新层表记录
      pLayerTblRcd->setName("MyLayer");   //设定图层名
      pLayerTblRcd->setIsFrozen(0);   // 图层解冻
      pLayerTblRcd->setIsOff(0);      // 打开图层
      pLayerTblRcd->setVPDFLT(0);   // 使用默认视口
      pLayerTblRcd->setIsLocked(0);   // 图层解锁
      AcCmColor color;    // AcCmColor是ACAD颜色管理类
      color.setColorIndex(1);      
      pLayerTblRcd->setColor(color);   // 图层颜色为红色
      // 为给新图层设置线型,要得到线型表记录的ID。
      //以下的代码演示如何得到并操作记录ID
      AcDbLinetypeTable *pLinetypeTbl;
      AcDbObjectId ltId;
      acdbHostApplicationServices()->workingDatabase()
            ->getSymbolTable(pLinetypeTbl, AcDb::kForRead);
      if ((pLinetypeTbl->getAt("DASHED", ltId))!= Acad::eOk)
      {   acutPrintf("\n未发现DASHED线型使用CONTINUOUS线型");
    // 每一个不完全空的图形数据库的线型表中都有线型名为CONTINUOUS 的默认记录
    // 试图删除该记录将会导致错误。
            pLinetypeTbl->getAt("CONTINUOUS", ltId);
      }
      pLinetypeTbl->close();
      pLayerTblRcd->setLinetypeObjectId(ltId);
      pLayerTbl->add(pLayerTblRcd);
      pLayerTblRcd->close();
      pLayerTbl->close();
    } else {
      pLayerTbl->close();
      acutPrintf("\nlayer already exists");
    }
}
以上只是对ACAD图形数据库的最基本介绍,图形数据库知识是ARX程序设计的基础和核心,以后还要对此进行更为深入的解释。今天就先说这些了。下一篇文章将对以上两个例子进行逐语句的详细解释,并且讨论与之相关的一些内容。另外,还要简单地介绍VC6和VC.NET的ARX程序不同之处。

goldenshin 发表于 2003-5-27 13:37:00

good

webhun 发表于 2003-6-11 14:05:00

VC功底太差,看过后还是一头雾水,不过基本思路已收到。要补下VC才行。

Taoyimaier 发表于 2003-6-18 00:35:00

等着你的下一篇

不过不要急,最重要的仍然时内容,呵呵,第一篇我打印出来了,

立尽斜阳 发表于 2003-8-27 17:34:00

为 什么我看不到精华帖子??

mccad 发表于 2003-8-27 22:10:00

立尽斜阳发表于2003-8-27 17:34:00static/image/common/back.gif为 什么我看不到精华帖子??



因为你是初级用户,需要努力加油。

just_soso 发表于 2003-9-2 20:25:00

要怎么才能看啊

gjliang 发表于 2003-9-10 15:32:00

哎哟,我也看不到,积分怎样积啊?

清醒 发表于 2003-9-27 10:29:00

茅塞顿开,很多很easy的却不明白的问题搞懂了,谢谢贴主。

/ps 看贴要回贴。。。。。。。

huangyhg 发表于 2003-10-22 21:25:00

类、抽象类、对象和实例
这几个概念他们有什么区别?(越学越糊涂..................)
页: [1] 2 3 4 5 6 7
查看完整版本: [连载]ObjectARX程序设计入门(1)