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