ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

AutoLISP图元查询:CAD二次开发的核心技术与实战指南

2026/8/19 16:02:01 拓冰建站 浏览量
AutoLISP图元查询:CAD二次开发的核心技术与实战指南 1. 从“黑盒”到“白盒”为什么AutoLISP图元查询是CAD二次开发的基石如果你在AutoCAD里画过图尤其是需要批量处理图纸的时候肯定有过这样的念头能不能让电脑自动识别出这张图里所有的墙线或者把所有标注文字小于某个值的都找出来改掉又或者统计一下这张图纸里用了多少种不同的图层这些看似简单的需求背后都指向同一个核心操作——查询图元信息。AutoCAD本身是一个强大的图形数据库我们看到的每一条线、每一个圆、每一段文字在它的内部都是一个结构化的“图元”Entity携带着海量的属性信息。对于普通用户这些信息是“黑盒”我们只能通过属性面板PROPERTIES一个个点开看。但对于开发者尤其是使用AutoLISP的开发者查询图元信息就是打开这个黑盒的钥匙。它让你写的程序不再是“盲人摸象”而是能“看清”图纸里的每一个对象知道它是什么、在哪里、有什么特性。可以说不会查询图元信息AutoLISP编程就寸步难行你写的脚本只能操作你手动选中的那几个对象毫无智能和批量处理能力可言。我接触过很多刚开始学AutoLISP的朋友他们往往卡在第一步代码写出来了但不知道如何“告诉”程序要去处理哪些图形。答案就是通过查询。无论是简单的图形统计、复杂的智能标注还是自动化出图流程第一步永远是“找到目标”。今天我就结合自己十多年的踩坑经验把AutoLISP里查询图元信息的门道掰开揉碎了讲清楚让你不仅能写出能跑的代码更能写出高效、健壮、易于维护的脚本。2. 理解AutoCAD的数据结构图元、DXF组码与选择集在动手写代码之前我们必须先理解AutoCAD是如何在内存中组织图形数据的。这就像你要去图书馆找书总得先知道图书馆用的是中图法还是杜威法书是按类别放还是按作者放。2.1 图元图形数据库的基本单元在AutoCAD中所有可见的图形对象直线、圆、多段线、文字、块参照等以及一些不可见对象如图层、线型、标注样式等都被抽象为“图元”。每个图元在AutoCAD内部都有一个唯一的标识符我们称之为“图元名”Entity Name。这个图元名不是我们手动赋予的而是AutoCAD在创建对象时自动生成的类似于数据库里的主键。在AutoLISP中我们通过函数如entlast获取最后一个创建的图元名或entsel让用户点选一个图元来获得这个“句柄”有了它我们才能对图元进行后续的查询和修改操作。2.2 DXF组码图元的“身份证”和“属性清单”如果说图元名是对象的“身份证号”那么DXFDrawing Interchange Format组码就是这张身份证上所有的详细信息包括姓名、性别、出生年月、住址等等。DXF是AutoCAD用于与其他CAD系统交换数据的文本格式而组码就是这种格式中用于标识数据类型的数字代码。对于AutoLISP开发者而言我们不需要关心整个DXF文件只需要理解每个图元的所有属性都通过一组“组码-值”对来存储和访问。例如组码0表示图元类型其值可能是LINE、CIRCLE、TEXT。组码8表示图层其值是该图层名的字符串如0或WALL。组码10表示起点坐标对于直线或圆心坐标对于圆其值是一个三维点坐标表如(100.0 200.0 0.0)。组码40表示半径对于圆或文字高度对于文字。为什么必须懂组码因为AutoLISP中最重要的信息查询函数entget返回的就是一个由(组码 . 值)对构成的关联表。你不认识组码就根本看不懂entget返回的数据更谈不上从中提取你需要的信息。注意不同图元类型的组码含义可能不同。例如组码10对直线是起点对圆是圆心对文字是插入点。查询时务必结合图元类型组码0来解读数据。AutoCAD的帮助文档中有完整的《DXF参考》这是我们的“新华字典”必须常备。2.3 选择集批量操作的“容器”单个图元的查询是基础但实际工作中我们几乎总是在处理一批图元。AutoLISP用“选择集”Selection Set来容纳多个图元名。你可以把选择集想象成一个临时数组或列表里面装着一堆图元名。创建选择集的核心函数是ssget。这个函数功能极其强大它允许你通过多种“过滤器”来一次性获取符合条件的所有图元。(ssget)提示用户在屏幕上框选或点选。(ssget X)不提示用户直接搜索整个图形数据库模型空间和布局空间。(ssget ‘((0 . “CIRCLE”)))搜索整个数据库但只选择圆0是组码CIRCLE是值。(ssget ‘((8 . “WALL”)))搜索整个数据库只选择在“WALL”图层上的图元。ssget配合过滤器列表是实现智能批量查询的核心手段。你不会想用循环去遍历数据库中的每一个图元再判断那样效率极低。正确的做法是用ssget和过滤器直接“捞”出你想要的图元集合。3. 核心查询函数实战从获取到解析理论铺垫完毕我们进入实战环节。AutoLISP查询信息的工具箱里最常用的就是三把“瑞士军刀”entget,ssget, 和ssnamex。下面我们通过具体场景来掌握它们。3.1entget解剖一个图元(entget ename)是查询的起点它接收一个图元名返回该图元的DXF组码关联表。假设我们画了一条从 (0,0) 到 (100,0) 的直线在“AXIS”图层上颜色随层。; 假设我们通过 (setq en (car (entsel “\n请选择一条直线: “))) 获得了图元名 en (setq edata (entget en))此时edata变量里存储的数据可能类似于( (-1 . 图元名: 7ffff706880) ; 图元名本身通常由-1组码表示 (0 . “LINE”) ; 图元类型 (330 . 图元名: 7ffff7039f0) ; 软指针指向所有者通常为模型空间块 (5 . “4F”) ; 句柄固定标识 (100 . “AcDbEntity”) ; 子类标记 (67 . 0) ; 模型空间标志0为模型空间1为布局空间 (410 . “Model”) ; 布局选项卡名 (8 . “AXIS”) ; 图层名 (100 . “AcDbLine”) ; 子类标记Line类 (10 0.0 0.0 0.0) ; 起点坐标 (11 100.0 0.0 0.0) ; 端点坐标 (210 0.0 0.0 1.0) ; 拉伸方向通常为Z轴 )如何从中提取信息使用assoc函数。它像查字典一样根据组码找到对应的(组码 . 值)对。(setq layer (cdr (assoc 8 edata))) ; 返回 “AXIS” (setq startpt (cdr (assoc 10 edata))) ; 返回 (0.0 0.0 0.0) (setq endpt (cdr (assoc 11 edata))) ; 返回 (100.0 0.0 0.0) (setq enttype (cdr (assoc 0 edata))) ; 返回 “LINE”实操心得assoc返回的是整个点对cdr才是取出值。对于坐标点表直接cdr即可。对于嵌套的复杂数据如扩展数据、多段线顶点需要更复杂的处理这是进阶内容。3.2ssget与过滤器精准“捕捞”场景我们需要找到图中所有在“DOOR”图层上且是“INSERT”块参照类型的图元也就是所有的门块。(setq ss (ssget “X” ‘((0 . “INSERT”) (8 . “DOOR”))))这行代码执行后ss就是一个包含了所有符合条件的门块的选择集。如果图中没有ss则为nil。过滤器列表详解((0 . “INSERT”) (8 . “DOOR”))是一个点对列表。它表示的逻辑关系是“与”AND即图元必须同时满足是块参照且在DOOR图层上。更复杂的过滤我们还可以加入关系运算符。(-4 . “”) (40 . 100.0))筛选半径或文字高度等小于100的图元。(-4 . “”) (62 . 1) (-4 . “”) (62 . 5))筛选颜色索引号在1到5之间的图元-4是关系运算开始/结束的组码。(-4 . “OR”) (0 . “CIRCLE”) (0 . “ARC”) (-4 . “OR”))筛选圆或圆弧。这里用到了逻辑运算符OR和OR。踩坑记录ssget “X”会搜索整个数据库包括冻结、关闭图层上的图元但不包括锁定图层上的图元。如果你发现程序“漏掉”了一些明明存在的图元首先检查它们所在的图层是否被锁定了。对于锁定图层上的图元需要先解锁图层或者用ssget的窗口选择模式配合(ssget “_:L”)之类的选择集模式字符串忽略图层锁定来处理但这通常需要用户交互。3.3 遍历选择集处理每一个“战利品”获取选择集只是第一步我们通常需要遍历其中的每一个图元进行信息读取或修改。(if ss ; 先判断选择集是否有效 (progn (setq i 0) ; 初始化计数器 (setq sslen (sslength ss)) ; 获取选择集长度 (while ( i sslen) (setq en (ssname ss i)) ; 获取选择集中第i个图元的图元名 (setq edata (entget en)) ; 获取该图元的数据 ; 在这里对edata进行处理例如打印信息 (princ (strcat “\n图元类型” (cdr (assoc 0 edata)))) (setq i (1 i)) ; 计数器加1 ) (princ (strcat “\n共处理了 “ (itoa sslen) “ 个图元。”)) ) (princ “\n未找到符合条件的图元。”) )这是一个标准的遍历循环模板。ssname函数根据索引从选择集中取出图元名。切记选择集的索引是从0开始的。性能小技巧如果选择集非常大成千上万个图元在循环内频繁调用princ打印信息会严重拖慢速度。在实际发布程序中应该将信息收集到列表里循环结束后再统一输出或者直接进行修改操作而不输出。4. 进阶查询与信息挖掘超越基础属性掌握了基础查询我们就可以解决更复杂的问题。图元的信息远不止图层、坐标这些基础属性。4.1 查询嵌套对象块参照与属性块参照INSERT是一种特殊的图元它本身有插入点、旋转角度等属性但它还“包含”着块定义中的多个子图元。更复杂的是带有属性的块ATTRIB。; 假设 en 是一个块参照的图元名 (setq edata (entget en)) (if ( (cdr (assoc 0 edata)) “INSERT”) (progn (setq blkname (cdr (assoc 2 edata))) ; 获取块名 (princ (strcat “\n块名” blkname)) ; 获取该块参照的属性如果存在 (setq atts (entnext en)) ; entnext 获取该图元之后的下一个图元在块参照中第一个子图元就是属性或图元 (while (and atts ( (cdr (assoc 0 (entget atts))) “ATTRIB”)) ; 这是一个属性图元 (setq attdata (entget atts)) (setq tag (cdr (assoc 2 attdata))) ; 属性标记TAG (setq value (cdr (assoc 1 attdata))) ; 属性值VALUE (princ (strcat “\n属性标记” tag “, 值” value)) (setq atts (entnext atts)) ; 继续查找下一个子图元 ) ) )这里的关键是entnext函数它用于遍历块定义或复杂图元如多段线的子图元链表。对于属性块其结构通常是INSERT图元 - 第一个ATTRIB属性 - 第二个ATTRIB属性 - ... - SEQEND图元表示属性序列结束。通过循环判断图元类型是否为“ATTRIB”我们可以提取所有属性值。4.2 查询非图形数据扩展字典与XData有时程序需要附着一些自定义信息到图元上这些信息不显示在图形中但可以被程序读取。这就是扩展数据XData和扩展字典Extension Dictionary的用途。扩展数据XData通过(entget ename ‘(“APPNAME”))来查询。“APPNAME”是你的应用程序名。XData以特定的组码范围1000-1071存储。处理起来比较繁琐适用于存储少量简单数据。扩展字典Extension Dictionary更现代、更强大的方式。图元数据中如果存在组码102其值为“{ACAD_XDICTIONARY”后面会跟着一个字典的图元名。通过这个字典可以存储任意复杂的自定义对象。查询它需要多层entget和dictsearch/dictnext操作是高级开发的内容但思路是先找到图元的扩展字典图元名然后像遍历文件夹一样遍历这个字典里的条目。实操心得对于大多数日常自动化需求基础属性和块属性查询已经足够。只有当你需要开发一个独立的插件或系统需要在图元间建立复杂的关联关系时才需要考虑扩展字典。XData由于其复杂性在新项目中已较少使用。4.3 利用ssnamex获取更详细的选择信息ssnamex是一个被低估的函数。ssname只返回图元名而ssnamex返回一个列表描述图元是如何被加入选择集的。这对于需要知道用户是通过“窗交”、“圈围”还是“栏选”等方式选择图元的高级交互程序非常有用。(setq ss (ssget)) ; 用户用窗口选择 (setq info (ssnamex ss)) ; 获取详细信息info的格式比较复杂是一个嵌套列表包含了选择方法如“F”表示栏选“W”表示窗口选择等以及对应的图元名列表。在需要精确还原用户选择过程或进行复杂的选择集过滤时这个函数能派上大用场。5. 实战案例编写一个图纸信息统计脚本让我们综合运用以上知识编写一个实用的脚本统计当前图纸中所有文字TEXT和MTEXT的数量并按图层分组列出。(defun C:COUNT_TEXT_BY_LAYER ( / ss i en edata layer cnt dict key) (princ “\n正在统计图纸中的文字TEXT/MTEXT信息...”) ; 步骤1创建过滤器选择所有单行文字和多行文字 (setq ss (ssget “X” ‘((-4 . “OR”) (0 . “TEXT”) (0 . “MTEXT”) (-4 . “OR”) ) ) ) (if (not ss) (progn (princ “\n图中未找到任何文字对象。”) (return) ) ) ; 步骤2初始化一个字典用关联表模拟用于存储图层-数量对 (setq dict ‘()) (setq i 0) (setq sslen (sslength ss)) (while ( i sslen) (setq en (ssname ss i)) (setq edata (entget en)) (setq layer (cdr (assoc 8 edata))) ; 获取图层名 ; 步骤3更新字典。如果图层已存在数量1否则新建条目。 (if (setq cnt (assoc layer dict)) (setq dict (subst (cons layer (1 (cdr cnt))) cnt dict)) (setq dict (cons (cons layer 1) dict)) ) (setq i (1 i)) ) ; 步骤4输出统计结果 (princ (strcat “\n\n文字统计完成共找到 “ (itoa sslen) “ 个文字对象。”)) (princ “\n”) (foreach item dict (setq key (car item)) (setq cnt (cdr item)) (princ (strcat “\n图层 [“ key “] : “ (itoa cnt) “ 个”)) ) (princ “\n”) (princ) )代码解读与避坑点过滤器使用OR...OR逻辑运算符一次性选择两种类型的文字比分别选择两次效率高。选择集判空(if (not ss) …)是必须的健壮性检查。用户可能在没有文字的图纸中运行此命令。数据存储我们用一个关联表dict来模拟字典。(assoc layer dict)查找该图层是否已记录。subst函数用于更新关联表中某个项的值。这是LISP中处理此类分组统计的经典模式。输出格式化使用strcat拼接字符串\n换行让输出结果清晰易读。这个脚本虽然简单但涵盖了从过滤、遍历、信息提取到数据汇总的完整流程。你可以在此基础上轻松扩展例如统计不同高度的文字、不同内容的文字等等。6. 性能优化与常见陷阱当图纸变得复杂图元数量达到数万甚至数十万时查询操作的性能就至关重要。以下是一些关键优化点和常见陷阱。6.1 减少选择集遍历次数错误示范为了找“A”图层上的圆和“B”图层上的矩形你写了两个循环分别用(ssget “X” ‘((0 . “CIRCLE”) (8 . “A”)))和(ssget “X” ‘((0 . “LWPOLYLINE”) (8 . “B”)))。这意味着AutoCAD要扫描整个数据库两次。正确做法尽量合并条件用更宽泛的条件一次性选出所有候选图元然后在LISP循环内部进行精细判断。; 一次性选出所有圆和多段线 (setq ss_all (ssget “X” ‘((-4 . “OR”) (0 . “CIRCLE”) (0 . “LWPOLYLINE”) (-4 . “OR”)))) (if ss_all (progn (setq i 0) (repeat (sslength ss_all) (setq en (ssname ss_all i)) (setq edata (entget en)) (setq layer (cdr (assoc 8 edata))) (cond ((and ( (cdr (assoc 0 edata)) “CIRCLE”) ( layer “A”)) ; 处理A图层的圆 ) ((and ( (cdr (assoc 0 edata)) “LWPOLYLINE”) ( layer “B”)) ; 处理B图层的多段线 ) ) (setq i (1 i)) ) ) )虽然循环内的判断多了但整个图形数据库只被扫描了一次对于大图纸性能提升是数量级的。6.2 善用ssget的过滤条件ssget的过滤器是在数据库引擎层面执行的速度远快于用LISP代码遍历后再判断。尽可能把筛选条件写在过滤器中而不是写在循环里。例如想找半径大于50的圆一定要用(-4 . “”) (40 . 50.0))而不是先选出所有圆再在循环里判断半径。6.3 警惕内存泄漏及时删除选择集选择集也是AutoCAD的一种对象会占用内存。用ssget创建的选择集如果不再使用应该用(sssetfirst nil nil)或显式地将其设置为nil来释放。虽然在小脚本中不明显但在长时间运行的或循环调用的程序中不释放选择集会导致内存占用不断增长。(setq ss (ssget “X” ‘((0 . “CIRCLE”)))) ; … 处理ss … (setq ss nil) ; 处理完毕后释放选择集6.4 坐标系与变换entget返回的坐标值如组码10, 11通常是**世界坐标系WCS**下的值。但是如果图元位于一个嵌套块中或者用户坐标系UCS被旋转过直接使用这些坐标进行计算可能会出错。常见陷阱你想计算一条线在当前视图下的长度直接用了WCS坐标但用户可能旋转了UCS。此时你需要使用trans函数进行坐标转换。(setq startpt_wcs (cdr (assoc 10 edata))) ; WCS起点 (setq startpt_ucs (trans startpt_wcs 0 1)) ; 从WCS(0)转换到当前UCS(1)trans函数的第二个和第三个参数是“从”和“到”的坐标系代码。0代表WCS1代表当前UCS。在处理涉及用户交互或显示的程序时务必考虑坐标系问题。查询图元信息就像给AutoCAD这个庞大的图形数据库装上了“眼睛”和“大脑”。从最基础的entget拆解一个图元到用ssget配合过滤器进行精准的批量“捕捞”再到处理块、属性等复杂对象每一步都建立在对其底层数据模型DXF组码的深刻理解之上。我见过太多脚本因为低效的遍历或错误的数据解读而运行缓慢甚至崩溃。掌握本文介绍的核心函数、理解其背后的原理、并牢记那些性能陷阱和常见坑点你的AutoLISP编程能力将完成一次质的飞跃。记住所有高效的自动化都始于一次精准的查询。当你能够随心所欲地获取图纸中的任何信息时你会发现让AutoCAD按照你的想法自动工作的那扇大门才真正被打开了。