Autolisp 没有为某些用户正确设置块属性

Autolisp not setting block attribute correctly for some users

第一次提问,希望我对问题的描述足够好。

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

您所描述的行为的罪魁祸首很可能是 ATTREQ system variable, which determines whether the user would receive prompts for attribute values as part of the 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 向命令提供点数据时,点将受到提供点时处于活动状态的任何对象捕捉模式的影响。我在回答 .

中更详细地描述了这一点

避免这种情况的最简单方法是使用 "_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

还有其他可能的改进也可以实施,例如:

  • 通过使用 ActiveX insertblock 方法或 entmake/ 消除对调用标准 AutoCAD 命令(在本例中为 INSERT 命令)的依赖entmakex 将 DXF 数据直接写入图形数据库的功能。

  • 通过引用它们的属性标记名称来填充属性,以便消除对块引用中遇到属性引用的顺序的依赖(可以通过使用 BATTMAN 命令)。

  • 使用 'dynamic default'(如我的 tutorial 中所述),并可能存储绘图会话之间的默认值。