第一次提问,所以希望我能很好地描述这个问题。
我们公司有一个 Autolisp 代码,几个人使用相同版本的 AutoCAD,但对于某些用户,lisp 已停止正常运行。
lisp的功能如下:
这应该导致一个带有标记的块和具有以下格式(前缀)的文本(如果数字不包含三个数字,则可能是中间部分)(运行数字),例如PT001 或 PX100。
然而,一些用户并没有这样做,而是遇到了缺少前缀和数字的情况,并开始只看到上述可能的文本中间部分,而其他时候,同一用户可能会体验到只显示了前缀。标记按原样显示,但文本不会按预期工作。
非常感谢在分析下面的代码缺陷方面的任何帮助。
如果代码看起来“完美无缺”,我认为块或其属性存在问题。
-E
(defun c:pointnumber()
(setvar "ATTDIA" 0)
(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: "))
(setq point 1)
(while (/= point nil)
(setq point (getpoint "\nChoose a point: "))
(if (/= point nil)
(progn
(setq inr (itoa nr))
(if (< nr 100) (setq md "0"))
(if (< nr 10) (setq md "00"))
(if (> nr 99) (setq md ""))
(setq ph (strcat px md inr))
(command "insert" "pointnumber" point sc sc 0 ph)
(setq nr (+ nr ic))
)
)
)
(setvar "ATTDIA" 1)(princ)
)
最佳答案
您当前的代码存在许多问题:其中一些可能仅仅被认为是不好的做法,如果用户使用无效数据进行响应,一些将导致程序失败,而另一些将导致程序失败或出现意外行为,具体取决于执行程序的 AutoCAD 环境的设置。
1. 攻击
您所描述的行为的罪魁祸首可能是 ATTREQ
系统变量,它确定用户是否会收到作为 INSERT
一部分的属性值提示。命令。如 ATTREQ=0
当程序运行时,块将插入其默认属性值。
您可以通过存储此系统变量的当前值并将其设置为 1
来确保环境之间的行为一致。在调用 INSERT
之前命令(确保发出属性提示),然后在命令之后或程序结束时恢复原始值。
例如:
(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 退出程序也会导致一个错误。
您可以通过定义本地错误处理程序来实现这一点,正如我在 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
还有其他可能的改进也可以实现,例如:INSERT
消除对标准 AutoCAD 命令(在本例中为 insertblock
命令)调用的依赖方法或 entmake
/entmakex
将 DXF 数据直接写入绘图数据库的函数。BATTMAN
命令在每个图形的基础上进行修改)。关于attributes - Autolisp 没有为某些用户正确设置 block 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65489757/