首页 > 解决方案 > Autolisp 没有为某些用户正确设置块属性

问题描述

第一次提问,所以希望我能很好地描述这个问题。

我们公司有一个 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)
)

标签: attributesblockautocadautolisp

解决方案


您当前的代码存在许多问题:其中一些可能仅仅被认为是不好的做法,如果用户使用无效数据进行响应,一些会导致程序失败,还有一些会导致程序失败或行为异常,具体取决于执行程序的 AutoCAD 环境的设置。

1. ATTREQ

您描述的行为的主要罪魁祸首可能是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 向命令提供点数据时,该点将受到在提供该点时处于活动状态的任何对象捕捉模式的影响。我在这里的回答中更详细地描述了这一点。

避免这种情况的最简单方法是使用"_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. 局部变量与全局变量

目前,程序中的所有变量都是全局变量:也就是说,它们是在文档(绘图)命名空间中定义的,并且即使在程序完成执行后也会保留它们的值。

正如我在本地化变量教程中所描述的,如果此类变量无意中与其他程序使用的全局变量共享名称,或者当程序在循环中构造列表或其他累积数据结构时,这可能会导致问题。

除非程序的正确操作需要使用全局变量,否则我建议将这些变量声明为函数的本地变量,例如:

(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退出程序也会导致错误。

您可以通过定义本地错误处理程序来完成此操作,正如我在错误处理教程中所描述的那样。如果在程序执行期间遇到错误,则会评估本地错误函数,因此您可以在此函数的定义中包含表达式以将 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通过使用 ActiveXinsertblock方法或entmake/entmakex函数将 DXF 数据直接写入图形数据库,从而消除对标准 AutoCAD 命令(在本例中为命令)的调用的依赖。

  • 通过引用属性标记名称来填充属性,以消除对在块引用中遇到属性引用的顺序的依赖(可以通过使用BATTMAN命令在每个图形的基础上进行修改)。

  • 使用“动态默认值”(如我的教程中所述),并可能在绘图会话之间存储默认值。


推荐阅读