您当前的代码存在许多问题:其中一些可能只是被认为是不好的做法,如果用户使用无效数据进行响应,一些会导致程序失败,还有一些会导致程序失败或行为异常取决于执行程序的 AutoCAD 环境的设置。
1。 ATTREQ
您所描述的行为的罪魁祸首可能是ATTREQ 系统变量,它决定用户是否会收到作为INSERT 命令的一部分的属性值提示。如果在程序运行时ATTREQ=0,则会插入带有其默认属性值的块。
您可以通过在调用INSERT 命令之前存储此系统变量的当前值并将其设置为1(以确保发出属性提示),然后恢复原始值来确保环境之间的行为一致跟随命令或程序结束。
例如:
(defun c:test ( / atr )
(setq atr (getvar 'attreq))
(setvar 'attreq 1)
;; ... Do your thing
(setvar 'attreq atr)
(princ)
)
2。操作系统模式
通过 AutoLISP 向命令提供点数据时,该点将受到在提供该点时处于活动状态的任何对象捕捉模式的影响。我在回答 here 中更详细地描述了这一点。
避免这种情况的最简单方法是使用 "_non" 或 "_none" 对象捕捉修改器来指示 AutoCAD 忽略所有对象捕捉模式以进行后续点输入,例如:
(command "insert" "pointnumber" "_non" point sc sc 0 ph)
3。用户输入
您应该考虑缺少用户输入或无效的用户输入以避免程序执行期间的错误 - 这很容易通过使用if 语句或initget 函数来实现,例如:
(initget 7) ;; Prevents Enter, zero, or negative numbers
(setq sc (getreal "\nEnter scale: "))
或者:
(initget 6)
(if
(and
(setq sc (getreal "\nEnter scale: "))
(setq px (getstring "\nSet prefix for point number: "))
(setq nr (getint "\nThe number for the first point: "))
(setq ic (getint "\nIncrement of the number: "))
)
;; ... Do your thing
)
或者,您可以使用我在Prompting with a Default Option 的教程中描述的方法之一为每个提示配置默认值,例如:
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0)))
4。局部变量与全局变量
目前,您程序中的所有变量都是全局变量:也就是说,它们是在文档(绘图)命名空间中定义的,并且即使在程序完成执行后也会保留它们的值。
正如我在 Localising Variables 的教程中所描述的,如果此类变量无意中与其他程序使用的全局变量共享名称,或者当程序在循环中构造列表或其他累积数据结构时,这可能会导致问题.
除非程序的正确运行需要使用全局变量,否则我建议将这些变量声明为函数的局部变量,例如:
(defun c:pointnumber ( / ic inr md nr ph point px sc ) ;; Local variables
;; ...
)
5。检查区块是否存在
将块名称直接提供给INSERT 命令假定该块的定义已存在于活动图形中,或者具有该文件名的图形存在于工作目录或 AutoCAD 支持文件搜索路径中 -如果两个条件都不满足,则INSERT命令在程序执行过程中会出错。
因此,您可以预先测试这些条件,如果未找到该块,则通知用户,否则继续执行其余操作:
(if
(or
(tblsearch "block" "pointnumber") ;; Checks for existing definition
(findfile "pointnumber.dwg") ;; Checks for drawing file
)
;; ...
)
您还可以使用cond 函数代替一系列if/else 表达式。
6。出错时重置环境
由于您在程序执行期间更改系统变量值,因此您应确保在程序执行期间发生错误时将用户的 AutoCAD 环境重置为其原始状态 - 请注意用户按 Esc kbd> 退出程序也会导致错误。
您可以通过定义本地错误处理程序来完成此操作,正如我在 Error Handling 的教程中所描述的那样。如果在程序执行期间遇到错误,则会评估本地错误函数,因此您可以在此函数的定义中包含表达式以将 AutoCAD 环境重置为其原始状态 - 在您的情况下,这将涉及重置ATTDIA系统变量。
把它们放在一起
;; Define function, declare local variables
(defun c:pointnumber ( / *error* bn ic nr ns pt px sc vl vr )
;; Define local error function to reset system variables on error
(defun *error* ( msg )
(mapcar 'setvar vr vl) ;; Reset list of system variables
(if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
(princ (strcat "\nError: " msg))
) ;; end if
(princ)
) ;; end defun
;; Define block name
(setq bn "pointnumber")
(if (or (tblsearch "block" bn) ;; Definition in drawing
(findfile (strcat bn ".dwg")) ;; Drawing file in support path
) ;; end or
(progn
(initget 6) ;; Prevents 0 & negatives
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0))
px (getstring "\nSpecify prefix <none>: ")
) ;; end setq
(initget 4) ;; Prevents negatives
(setq nr (cond ((getint "\nSpecify starting number <1>: ")) (1)))
(initget 6) ;; Prevents 0 & negatives
(setq ic (cond ((getint "\nSpecify increment <1>: ")) (1)))
(setq vr '(attreq attdia cmdecho) ;; List of system variables
vl (mapcar 'getvar vr) ;; Store current values
) ;; end setq
(mapcar 'setvar vr '(1 0 0)) ;; Set system variables appropriately
(while (setq pt (getpoint "\nSpecify point <exit>: "))
(setq ns (itoa nr)
nr (+ nr ic)
)
(repeat (- 3 (strlen ns)) (setq ns (strcat "0" ns))) ;; Pad to 3 digits
(command "_.-insert" bn "_S" sc "_R" "0" "_non" pt (strcat px ns))
) ;; end while
(mapcar 'setvar vr vl) ;; Reset list of system variables to their original values
) ;; end progn
;; Else the block was not defined/found
(princ (strcat "\nThe block \"" bn "\" is not defined in the active drawing and cannot be found."))
) ;; end if
(princ) ;; Suppress the value returned by the last evaluated expression
) ;; end defun
还可以实施其他可能的改进,例如:
-
通过使用 ActiveX insertblock 方法或 entmake/entmakex 函数将 DXF 数据直接写入图形数据库,从而消除对标准 AutoCAD 命令(在本例中为 INSERT 命令)的调用的依赖.
-
通过引用属性标记名称来填充属性,从而消除对在块引用中遇到属性引用的顺序的依赖(可以通过使用BATTMAN 命令在每个图形的基础上进行修改) )。
-
使用“动态默认值”(如我的 tutorial 中所述),并可能在绘图会话之间存储默认值。