最近折腾了几天autolisp二次开发;做了一个批量统计数据的程序。为方便后期复盘整理,现将整个过程记录如下。
需求和应用场景分析
一个好的产品需求和应用场景分析有助于我们确定目标;排除过程中的很多干扰。但其实就是两件事:
产品的功能和目的:
该类程序主要用于批量操作dwg格式文档,自动统计图纸中所需的信息;并将数据放置于excel表格。从而达到提高统计效率,避免手工统计出错的目的。
产品的使用情景:
本实例主要用于以下场景: 在项目出图完成之后,整理加工明细过程中;自动统计发包图纸的图号和零件数量并放置于excel表格中。
例如:有一个项目有100张图需要发包,按以往操作需手工统计每张图纸的图号和对应的加工数量放置于excel表格中。而用程序实现则只需要选择该路径下的任意一张图纸;便可批量提取该路径下面的所有文件信息;极大提高效率。
主要功能模块分析
将整个产品拆分为功能模块能让我心里有底,大概了解项目进度。本次完成上述功能可以分以下三步走:
读取单张图纸信息:完成对单张图纸的操作;提取所需要的信息。
向excel表格中写入数据:完成将autocad中的数据写入excel表格中。
批量操作所有文件:对每张文件进行相同的操作;依次自动完成第一步和第二步。
选择开发工具
俗话说“工欲善其事,必先利其器”;一个合适的开发工具能帮助我们更快更好的完成任务。
目前常用开发工具:
目前在autocad二次开发领域,开发工具主要有:autolisp、visual lisp、objectarx、vba、.net。每种工具的介绍网上一大堆;这里主要通过下表进行简单的比较:
根据目前现状确定开发工具
作为一枚初学者,经过一番了解之后我确定了本次使用Visuallisp作为开发工具。理由如下:
没使用过VB、.NET,而VC基本忘完了;目前也不打算捡起来。autocad自身支持lisp开发,并内嵌IDE。而其他开发工具都得单独搭建IDE。
资料收集
本次资料收集主要包括三个种类:书籍、视频、帮助文档。就本次学习中觉得比较好的资料主要有:
以上所有资料均已放置于网盘备份:
视频链接:/s/1vpmbFxpnmsdVZRUSzCqXZw 提取码:u4w6
文档链接:/s/1A832pFoUbHv1SvfksSq2BQ 提取码:5p0z
编码测试
lisp基础学习
当我们带有实际问题学习一项编程工具时,大可不必一步一步的学习。我们只需要针对我们可能会用到的知识进行学习了解。具体到本次编程过程中基础部分主要学习了以下内容:
《AUTOCAD完全应用指南 AUTOLISP+DCL+VISUAL LISP程序设计篇》的第一篇autolisp基础;学完基本能玩一些简单的命令;提取一些简单的特征。看了视频教程的前面的两集学习vlIDE的使用;以及11-15集学习了图元属性和选择集的相关知识。
尝试读取图纸表格中的数据
在对lisp有了一定了解之后开始尝试完成读取图表中的信息。
要了解所需信息在图纸中的位置;我们可以在一张打开的目标图纸中使用vlIDE中的使用图形库浏览功能找所需要的信息(当然也可以在autocad命令窗口中采用交互式命令结合群码得到所需的信息)。此处可以多尝试。
在得到所需信息的位置后;编写代码如下:
;;;读取图框列表信息 (defun c:read_A4()(setq a4_block (ssget "X" (list (cons 2 "A4"))))(setq box_inf (ssname a4_block 0))(setq a01 (entnext box_inf));取得对象:日期(setq data(cdr (assoc 1 (entget a01 ))));获得日期属性值(setq a02 (entnext a01));表面处理(setq finish(cdr (assoc 1 (entget a02))));获得表面处理方式的属性值(setq a03 (entnext a02));材料(setq material(cdr (assoc 1 (entget a03 ))))(setq a04 (entnext a03));比例(setq scale(cdr (assoc 1 (entget a04))))(setq a05 (entnext a04));数量(setq quantity(cdr (assoc 1 (entget a05 ))))(setq a06 (entnext a05));图号(setq files_no(cdr (assoc 1 (entget a06))))(setq a07 (entnext a06));版本(setq version(cdr (assoc 1 (entget a07 ))))(setq a08 (entnext a07));设备系列(setq project_no(cdr (assoc 1 (entget a08 ))))(setq a09 (entnext a08));设备名称(setq description(cdr (assoc 1 (entget a09 ))))(setq a10 (entnext a09));绘图者 (setq draw_name(cdr (assoc 1 (entget a10 ))));;;打印获得的数据。(print data)(print finish)(print material)(print scale)(print quantity)(print files_no)(print version)(print project_no)(print description)(print draw_name)(print )))
效果演示:
尝试向excel表格中写入数据**
在浏览《AUTOCAD完全应用指南 AUTOLISP+DCL+VISUAL LISP程序设计篇》目录时发现有一个实例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JVzKgF7w-1581742315346)(//02/15/MwIOnk8c2z4siTF.png)]
废话不多说,直接参照实例做一点点修改跑起来:
;;;向Excel文件中写入数据(defun c:write_to_excel()(vl-load-com) ;加载扩展的lisp函数(setq exlib "C:\\Program Files\\Microsoft Office\\Office15\\EXCEL.EXE");加载应用资源库(需根据机器安装路径调整);;导入Excel应用资源库并指定函数前缀(if (null MX-acos);判断资源库是否已加载(vlax-import-type-library:tlb-filename exlib:methods-prefix "MX-":properties-prefix "MX-":constants-prefix "MX-"))(setq XLobj (vlax-create-object "Excel.Application"));创建Excel应用程序对象(vla-put-visible XLobj 1) ;; (vla-put-visible XLobj :vlax-true);将创建的Excel应用程序对象XLobj显示出来 。关闭(vla-put-visible XLobj 0)或(vla-put-visible XLobj :vlax-flase)(setq wb-obj (vlax-invoke-method (vlax-get-property XLobj'WorkBooks) 'Add));新增一个空白工作簿对象,并指定给wb-obj(setq sheetobj (MX-get-activesheet wb-obj)) ;创建工作簿中工作表对象sheetobj,以便在各单元格中操作(setq cells (MX-get-cells sheetobj));创建工作窗体中单元格对象(setq ceobj-1-2(vlax-variant-value(MX-get-item cells 1 2))) ;创建单元格(1,2)对象(MX-put-value2 ceobj-1-2 "AutoCAD12");,写入文字"AutoCAD"(setq ceobj-2-2(vlax-variant-value(MX-get-item cells 2 2)));创建单元格(2,2)对象(MX-put-value2 ceobj-2-2 "SAKURA25");写入文字"SAKURA"(setq ceobj-3-2(vlax-variant-value(MX-get-item cells 3 2)));创建单元格(3,2)单象(MX-put-value2 ceobj-3-2 "ActiveX22");,并写入文字"ActiveX ";;取出单元格(1,2)对象的技巧(setq cell-1-2(vlax-variant-value (MX-get-value ceobj-1-2)))(princ "\nCELL-l-2 =")(princ cell-1-2) (setq cell-2-2(vlax-variant-value (MX-get-value ceobj-2-2)))(princ "\nCELL-2-2 = ")(princ cell-2-2) (setq cell-3-2(vlax-variant-value (MX-get-value ceobj-3-2)))(princ "\nCELL-3-2 = ")(princ cell-3-2)|;(setq filename(getfiled "请选取文件保存路径" "" "xls" 1)) ;选择excel文件保存路径和名称。(vlax-invoke-method wb-obj "Saveas" filename nil nil nil nil nil nil) ;保存文件(prin1 "写入数据成功" )(prin1))
效果演示:
批量处理
最开始以为批量处理应该最简单;就是用循环语句把下面流程执行n次:
把一张文件打开——>读取所需信息——>将信息写入表格——>关闭图纸
不过万万没想到;在autocad环境里面实现起来并不那么顺利。过程中进行了如下几个方案的尝试:
open/close 函数
用lisp 读取文件信息只能完成当前活动文档的操作;但是利用open/close函数虽然可以打开和关闭文件。但我却未找到能将打开的文件刷新为活动文档且不导致程序挂起的解决方案。所以这个方案我目前并未成功。
script脚本触发循环
在许多资料中都有介绍利用scr脚本完成lisp程序的循环的案例。也就是将write-line 将vlisp语句写入scr文件中;然后利用command 调用脚本执行。根据我自己的测试来看用scr脚本可以方便的打开和关闭图档,但是具体到这个程序上始终完成不了预想的功能。具体问题有以下两个:
单纯利用脚本打开和关闭文档时: 不能完成lisp程序与scr脚本的双向通讯问题。具体就是我在lisp程序使用command调用脚本打开一张文件后;不能让脚本告诉lisp程序打开图纸了;可以读取信息了。
把整个循环的语句全都写入scr脚本中;最后再执行脚本。但是这样也遇到两个问题:
① 貌似将excel执行相关的语句写入scr中;无法正确执行。一直提示 函数未定义(哪怕在scr脚本中再次利用 (vl-load-com) 加载一次也不行)。
②如果处理的图纸比较多,那么写到scr文件中的语句会非常
activex 多图档环境
前面两种方案失败后;完全没想法了。于是去网上扒了《VisualLisp开发人员手册》啃了一下午,明白了些东西;重点是下面一段话
接着又有方向了,去逛了几个论坛找到了应该是可以完成上述功能的语句:
(vla-open(vla-get-documents(vlax-get-acad-object)) filename
)
接着cad环境下测试却不能运行成功:
WTF 又不对,啥情况。网上搜索错误提示结果说是控件注册错误;语句没有问题(拜托朋友测试了他机器上是可以跑的)。接着也没有找到解决方案;这条路也断了。。。。绝望呀!
insert 命令
经历个三个失败方案后,就开始瞎逛论坛和各种google了;找了几篇文献看了一下。其中有一篇毕业论文《基于AutoLisp的AutoCAD图框批量处理技术研究》有点儿意思;发现既然人家能利用块命令将有多张图的一个文档输出为只有一张图的多个文档;那应该也可以反向操作。说干就干,找了一下块相关的命令(平时没用完全没印象);结果发现一个insert。官方介绍如下:
接着结合帮助文档测试了一下,发现可以将文档整个插入当前活动文档中。具体玩法:
(command "-insert" filename "0,0" "1" "0")
知道怎么玩批量之后就简单了;直接把前的两个子程序粘在一起,来个循环就跑起了:
(defun c:read_A4_to_Excel();;;系统变量初始化(setq CE(getvar "cmdecho")) ;将初始系统变量保存便于程序运行完成之后恢复(setq FD(getvar "filedia"))(setvar "cmdecho" 0) ;设置系统变量(setvar "filedia" 0);;;获取需要统计加工明细的路径(setq file(getfiled "请选取需要统计加工明细的文件" "" "dwg" 0)) ;指定统计文件(setq path(vl-filename-directory file));获取统计文件夹(setq file_list(vl-directory-files path "*.dwg" 0 ));获取文件夹内所有文件名(setq n(length file_list));获取列表长度;;;创建excel表格(vl-load-com) ;加载扩展的lisp函数(setq exlib "C:\\Program Files\\Microsoft Office\\Office15\\EXCEL.EXE");加载应用资源库;;导入Excel应用资源库并指定函数前缀(if (null MX-acos);判断资源库是否已加载(vlax-import-type-library:tlb-filename exlib:methods-prefix "MX-":properties-prefix "MX-":constants-prefix "MX-"))(setq XLobj (vlax-create-object "Excel.Application"));创建Excel应用程序对象 (vla-put-visible XLobj 1) ; (vla-put-visible XLobj :vlax-true)将创建的Excel应用程序对象XLobj显示出来;关闭(vla-put-visible XLobj 0)或(vla-put-visible XLobj :vlax-flase)(setq wb-obj (vlax-invoke-method (vlax-get-property XLobj'WorkBooks) 'Add));新增一个空白工作簿WorkBook对象,并指定给wb-obj(setq sheetobj (MX-get-activesheet wb-obj));创建工作簿中工作表对象sheetobj,以便在各单元格中操作(setq cells (MX-get-cells sheetobj));创建工作窗体中单元格对象|;(setq ceobj-1-2(vlax-variant-value(MX-get-item cells 1 1)));创建单元格对象,并写入图号(MX-put-value2 ceobj-1-2 "图号" )(setq ceobj-2-2(vlax-variant-value(MX-get-item cells 1 2))) ;创建单元格对象,并写入数量(MX-put-value2 ceobj-2-2 "数量")(setq i 0)(setq j 2)(setq k 1);;循环读取每个文件内容到excel表格(repeat n(setq id(nth i file_list));获取列表中的第一个元素(setq f1(strcat "*" path "\\" id));完整文件路径(command "-insert" f1 "0,0" "1" "0");以分解状态插入块(setq a4_block (ssget "X" (list (cons 2 "A4"))))(setq box_inf (ssname a4_block 0))(setq a01 (entnext box_inf));(setq data(cdr (assoc 1 (entget a01 ))))(setq a02 (entnext a01))(setq a03 (entnext a02))(setq a04 (entnext a03))(setq a05 (entnext a04))(setq a06 (entnext a05))(setq files_no(cdr (assoc 1 (entget a06)))) ; 图号(setq quantity(cdr (assoc 1 (entget a05 ))));数量;;写入数据(setq ceobj-1-2(vlax-variant-value(MX-get-item cells j k)));创建单元格对象,并写入图号(MX-put-value2 ceobj-1-2 files_no )(setq ceobj-2-2(vlax-variant-value(MX-get-item cells j (+ k 1)))) ;创建单元格对象,并写入数量(MX-put-value2 ceobj-2-2 quantity)(setq j(+ j 1)) (command "ERASE" "all" "") (setq i(+ i 1)))(setq filename (STRCAT path "\\files_no.xls")) ;excel构造文件保存路径(vlax-invoke-method wb-obj "Saveas" filename nil nil nil nil nil nil)(setvar "cmdecho" CE) ;恢复系统变量(setvar "filedia" FD)(princ "批量统计完成"))
效果演示: