Autolisp ไม่ได้ตั้งค่าแอตทริบิวต์บล็อกอย่างถูกต้องสำหรับผู้ใช้บางราย

Dec 29 2020

ผู้ถามครั้งแรกหวังว่าฉันจะอธิบายปัญหาได้ดีพอ

เรามีรหัส Autolisp ใน บริษัท ของเราซึ่งใช้โดยบุคคลหลายคนที่มี AutoCAD เวอร์ชันเดียวกัน แต่สำหรับผู้ใช้บางรายเสียงกระเพื่อมหยุดทำงานอย่างถูกต้อง

หน้าที่ของเสียงกระเพื่อมมีดังนี้:

  • ผู้ใช้เรียกใช้เสียงกระเพื่อม
  • โปรแกรมขอสิ่งต่อไปนี้:
    • ขนาดของบล็อก
    • คำนำหน้าสำหรับข้อความในบล็อก
    • หมายเลขรันสำหรับเอนทิตีบล็อกแรก
    • การเพิ่มขึ้นของหมายเลขวิ่ง
    • ตำแหน่งที่จะวางบล็อกแรก

สิ่งนี้ควรนำไปสู่บล็อกที่มีเครื่องหมายและข้อความที่มีรูปแบบต่อไปนี้ (คำนำหน้า) (ส่วนตรงกลางที่เป็นไปได้หากตัวเลขไม่ประกอบด้วยตัวเลขสามตัว) (หมายเลขวิ่ง) เช่น 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 จุดนั้นจะได้รับผลกระทบจากโหมด Object Snap ใด ๆ ที่ใช้งานอยู่ในขณะที่ระบุจุดนั้น ผมอธิบายเรื่องนี้ในรายละเอียดในคำตอบของฉันที่นี่

วิธีที่ง่ายที่สุดในการหลีกเลี่ยงปัญหานี้คือการใช้ตัวปรับแต่งสแน็ปวัตถุ"_non"หรือ"_none"อ็อบเจ็กต์เพื่อสั่งให้ AutoCAD ละเว้นโหมด Object Snap ทั้งหมดสำหรับอินพุตจุดที่ตามมาเช่น:

(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. ตัวแปรท้องถิ่นเทียบกับทั่วโลก

ปัจจุบันตัวแปรทั้งหมดในโปรแกรมของคุณเป็นตัวแปรส่วนกลางนั่นคือตัวแปรเหล่านี้ถูกกำหนดไว้ในเนมสเปซของเอกสาร (รูปวาด) และจะยังคงรักษาค่าไว้แม้ว่าโปรแกรมจะดำเนินการเสร็จสิ้นแล้วก็ตาม

ตามที่ฉันอธิบายไว้ในบทช่วยสอนเรื่อง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เพื่อออกจากโปรแกรมจะส่งผลให้ ข้อผิดพลาด

คุณสามารถทำได้โดยการกำหนดจัดการข้อผิดพลาดในท้องถิ่นตามที่ผมอธิบายในการกวดวิชาของฉันในการจัดการข้อผิดพลาด ฟังก์ชันข้อผิดพลาดในเครื่องจะได้รับการประเมินหากพบข้อผิดพลาดระหว่างการทำงานของโปรแกรมดังนั้นคุณจึงสามารถรวมนิพจน์ภายในคำจำกัดความของฟังก์ชันนี้เพื่อรีเซ็ตสภาพแวดล้อม 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

มีการปรับปรุงที่เป็นไปได้อื่น ๆ ที่สามารถนำไปใช้ได้เช่น:

  • การลบการพึ่งพาการเรียกไปยังคำสั่ง AutoCAD มาตรฐาน (ในกรณีนี้คือINSERTคำสั่ง) โดยใช้insertblockเมธอดActiveX หรือฟังก์ชันentmake/ entmakexเพื่อเขียนข้อมูล DXF โดยตรงไปยังฐานข้อมูลรูปวาด

  • การเติมแอ็ตทริบิวต์โดยการอ้างอิงชื่อแท็กแอ็ตทริบิวต์เพื่อลบการพึ่งพาลำดับที่พบการอ้างอิงแอ็ตทริบิวต์ภายในการอ้างอิงบล็อก (ซึ่งสามารถแก้ไขได้ตามรูปวาดโดยใช้BATTMANคำสั่ง)

  • การใช้ 'ค่าเริ่มต้นแบบไดนามิก' (ตามที่อธิบายไว้ในบทช่วยสอนของฉัน) และอาจจัดเก็บค่าของค่าเริ่มต้นระหว่างเซสชันการวาด