Autolispが一部のユーザーのブロック属性を正しく設定しない

Dec 29 2020

初めての質問なので、うまくいけば、私は問題を十分に説明しています。

弊社には、同じバージョンのAutoCADを使用する複数の個人が使用するAutolispコードがありますが、一部のユーザーにとっては、lispが正しく機能しなくなりました。

lispの機能は次のとおりです。

  • ユーザーがLispを実行する
  • プログラムは次のことを要求します。
    • ブロックのスケール
    • ブロック内のテキストのプレフィックス
    • 最初のブロックエンティティの実行番号
    • 実行中の番号の増分
    • 最初のブロックを配置する場所

これにより、マーカーと次の形式のテキスト(プレフィックス)(番号が3つの番号で構成されていない場合は中央のセクションになる可能性があります)(実行番号)、たとえば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)
)

回答

1 LeeMac Dec 30 2020 at 01:21

現在のコードにはいくつかの問題があります。その中には単に悪い習慣と見なされるものもあれば、ユーザーが無効なデータで応答するとプログラムが失敗するものもあれば、プログラムが失敗したり予期しない動作をしたりするものもあります。プログラムが実行される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. OSMODE

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
)

または、デフォルトオプションを使用したプロンプトに関するチュートリアルで説明した方法のいずれかを使用して、これらの各プロンプトのデフォルト値を構成することもできます。次に例を示します。

(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

次のような、他にも実装可能な改善点があります。

  • INSERTActiveXinsertblockメソッドまたはentmake/entmakex関数を使用してDXFデータを図面データベースに直接書き込むことにより、標準のAutoCADコマンド(この場合はコマンド)の呼び出しへの依存を取り除きます。

  • ブロック参照内で属性参照が検出される順序への依存を取り除くために、属性タグ名を参照して属性を設定します(BATTMANコマンドを使用して図面ごとに変更できます)。

  • '動的デフォルト'を使用し(私のチュートリアルで説明されているように)、描画セッション間でデフォルトの値を保存する可能性があります。