-
Las Vegas, Nevada
November 28 – December 1
Speaker Name: Randy Kintzley
Course: PG33-1
Course Description: AutoCAD Hacker’s Handbook
1
AutoCAD Hacker’s Handbook
Step into the mind of an Express Tools programmer and learn his favorite tricks for
programming with Lisp, VB and VBA. Some of the topics this course will cover are tips for
debugging, entity creation, finding information, techniques for accessing VB functionality from lisp,
as wel as tips on software design.
How to display a dialog version of a command from lisp
If you type LAYER AutoCAD will display the layer dialog. On the other hand; if you type
(command "layer") AutoCAD will invoke the command line version of layer. But... What if you
want the layer dialog to display from a lisp routine?
Here's how:
(initdia)
(command "layer")
The initdia function causes the next AutoCAD command to display the dialog version of the
command. Unfortunately it does not work with all commands. XREF is one command that does
NOT work with initdia. See the section on vla-sendcommand for a way to cheat and get the
dialog to appear...
Tips for Entity creation with Lisp:
o
Look at the entity list for existing entities, remove the unwanted information and cut and
paste
o
Make sure referenced symbol tables exist (with exception of layers)
o
Watch out for CECOLOR, CELTYPE…etc. For example: If you do not explicitly include
a color then the current color will be used. To specify, color ByLayer, use color 256.
Some debugging tips:
o
Putting in break points with getstring and printing results with print
o
Try it at the command line to see how it works
o
Comment closing parens to match opening ones
o
Use local variables when you can
o
Remember to initialize counters and other variables
2
Finding information:
o
Using the LSP command included with the Express Tools
o
DXF files
o
Using the utilities in rk-util.lsp:
o
LL (Lisp-List)
o
DXFHELP
o
OHELP (object help)
o
EDIFF (entity-diff)
Recommended programming practices:
o
Comment closing parentheses to match opening ones.
o
Divide and conquer. Divide the program up into separate tasks and write separate
functions to handle each of them. Remember; it’s not a contest to see who can nest the
most parentheses.
o
Use local variables
o
Pass in needed values
o
Return results. If more than one value return a list.
o
For functions that are likely to change; pass in a single list and extract the arguments on
the fly. NOTE: See “make-circle.lsp” for an example.
o
Avoid modifying acad2000.lsp or acad2000doc.lsp. Instead create an acad.lsp or
acaddoc.lsp.
o
Menu changes:
o
When possible, use partial menus instead of modifying the main acad.mnu.
o
If you do modify the main menu then put in easy to see comments to let you know
where the changes are so that you can easily move the changes to the next
AutoCAD menu when the next release comes out.
o
Look at other people’s code. Surf the web, read CAD mags …etc.
Some Menu tips
o
Buttons – AUX3 and AUX4… under used powerhouses.
o
Control (layer, color, dim, view, …etc.)
o
Context menu tricks with “Object_name” (also show c:newtext)
o
Diesel (Tom)
3
Error handling with the Express Tools error handler:
The Express Tools error handler is a good general purpose error handler that you can use in
your own lisp routines. Some of the benefits are, UNDO handling, SYSVAR management, and
extensibility.
o
Initialize using (acet-error-init (list …))
o
Always restore the original error handler (acet-error-restore)
o
UNDO flags
o
no undo - nil
o
undo begin and end - 0
o
undo on cancel - 1
o
Sysvars
o
set and store original value for restore later
o
pass nil to avoid changing a value but still save the value
For example, I will need to turn highlight off later but not yet, I want to wait until after I
ask them to select some objects.
Example:
;; highlight value does not change but its value
;; is saved so it can be restored later
(acet-error-init (list 1 '("highlight" nil)))
(setq ss (ssget))
(setvar "highlight" 0)
;; now turn highlight off
;; do some other stuff here....
(acet-error-restore)
;; restore original error and
highlight value
o
Common sysvar gotchas:
o
OSMODE - turn osnaps off
o
OSNAPCOORD - controls how coordinate input in scripts and lisp behave with osnap
settings. The default is to use explicitly entered coords unless lisp or script is active. In
other words, AutoCAD will, by default, honor osnap settings. You can turn
OSNAPCOORD to 1 to always use explicit entry within lisp and scripts.
o
PICKSTYLE - controls group selection. You can set it to 0 to pick just one object within
a group and not get the entire group.
o
UCSFOLLOW - turn it off if plan to mess with the UCS.
4
o
LIMCHECK - lisp can fail if limcheck is on.
o
ATTREQ - when inserting blocks with attributes.
o
PLINEWID - control polyline width (make sure you set it the way you want it)
o
PLINETYPE - be aware of what type of polylines AutoCAD creates with the pline
command.
Some common programming mistakes:
Mistake 1 - Locked layers:
Trying to process objects on Locked layers is a common mistake. You should either unlock
them or stop users from selecting objects on locked layers in the first place.
Tip:
(ssget ":L")
;; ":L" stops users from selecting objects on
locked layers
Mistake 2 - Objects selected are not in current space
Trying to run commands on objects that are not in the current space. This can happen when a
user selects “Al ” or “P” (Previous) and the selection set contains objects that are not in the current
space.
You can make ssget filter out objects that are not in the current space. The sample function
below generates a filter list that can be used with ssget to achieve this behavior.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;
;; Returns a list that can be used as the filter argument to ssget.
;; The filter restricts selection to objects that are in the current
space.
;;
(defun ssget-current-space-filter ( / flt )
; Group code note:
;
67 1 means paper space
;
67 0 is model
;
410 is the ctab (current layout)
(if (= 1 (getvar "cvport"))
(setq flt (list '(67 . 1)
(cons 410 (getvar "ctab"))
)
5
);setq then paper space
(setq flt (list '(67 . 0))) ; else model space
);if
flt
);defun ssget-current-space-filter
Here's are some examples of how you would typically use it:
(setq flt (ssget-current-space-filter))
;; restrict selection to any object in the current space
(ssget flt)
;; restrict selection to LINES in current space
(ssget (append '((0 . "LINE")) flt))
;; only allow objects in the current space that are not on locked
layers
(ssget ":l" flt)
Mistake 3 - Assuming a certain type of behavior based on your sysvar settings.
There is no one cure-al for this one. Always research the variable settings that affect a
command or groups of commands that you plan to use. Then make sure you set them the way
you want them and put them back when you are done.
Mistake 4 - Expecting one object type and getting another.
Don't just ask your user to select the proper type of object. Filter for that type of object
automatically. For example: If you are expecting TEXT then use ssget to mandate that the user
only select text. i.e. (ssget '((0 . "TEXT")))
Lisp performance tips:
o
cmdecho=0, highlight=0
o
Do as little as possible within loops. Prepare needed values in front of the loop so fewer
operations need to be done within the loop.
6
o
Entget tends to be slow. Hang on to the data as long as you need it instead of doing
multiple entgets.
o
When possible, use cons instead of append when building lists. It's much faster.
o
Use '=' instead of 'equal' for numeric values.
o
Structure if statements so that least costly conditional is executed first.
o
For the example below, lets say ‘doit’ is a simple T/nil flag and the find-the-perfect-
ent function is somewhat costly in terms of time. The following if statement will not call
find-the perfect-ent unless the doit flag is non-nil.
(if (and doit
(setq na (find-the-perfect-ent))
(setq ent (entget na))
)
(princ "\ndoing it")
);if
o
Avoid calling (gc) within a loop.
o
Improve load times: compile lsp files into fas files.
Example:
vlide
(vlisp-compile 'st "mystuff.lsp" "mystuff.fas")
Tips for optimizing list processing
Avoid this:
(setq ent […some entity list])
(setq lst […some list of real values])
(setq n 0)
(repeat (length lst)
(setq
x (nth n lst)
h (cdr (assoc 40 ent))
lst2 (append lst2 (list (* h x)))
)
(setq n (+ n 1))
)
What's wrong with the above code?
7
o
Can be done in fewer statements
o
Repeatedly sets 'h' to the same value. Should be done before entering the loop.
o
Uses append instead of cons
Something more like this is better:
(setq h (cdr (assoc 40 ent)))
;; set value ahead of time
(foreach x lst
;; use foreach to avoid need for a
counter variable
(setq lst2 (cons (* h x) lst2));; use cons instead of append
)
Or even better still:
(setq h (cdr (assoc 40 ent)))
;; set value ahead of
time
(setq lst2 (mapcar '(lambda (x) (* x h)) lst))
;; use mapcar
Error trapping with vl-catch-all-apply …How to catch a lisp error and continue
processing.
There are three functions that can be used together to give your lisp routines the ability to
catch errors and continue processing.
o
vl-catch-al -apply
o
vl-catch-all-error-p
o
vl-catch-all-error-message
The following example demonstrates the use of these functions. The example function asks
you to enter Color or Linetype and then takes you to a sub-prompt based on that answer. Using
traditional methods; If the user cancels at a sub-prompt, lisp will cancel the entire lisp command.
This example demonstrates how to catch the cancel and continue processing. The 'LType' option
uses traditional methods so if you cancel at that sub-prompt the entire command will be canceled.
If you enter the 'Color' option and then cancel at that sub-prompt, you will be taken back to the
main prompt. This behavior is achieved through the use of vl-catch-al -apply
(defun c:catchit ( / c lt )
(while (progn
(initget "Ltype Color")
(setq ans (getkword "\nEnter [Color/Ltype]: "))
)
8
(if (equal ans "Color")
(progn
;; call getstring using 'vl-catch-all-apply'
(setq result (vl-catch-all-apply 'getstring '("\nEnter a
color: ")))
(if (not (vl-catch-all-error-p result))
(setq c result)
(progn
;; print the error information
(princ (strcat "\nTrapped error: "))
(princ (vl-catch-all-error-message result))
(princ "\nContinuing processing...")
);progn else
)
);progn then
(progn
;; call getstring the standard way...
;; if the user cancels this, then it's all over.
(setq lt (getstring "\nEnter a lintype: "))
);progn else
);if
);while
(princ "\nYou entered Color=")(princ c)
(princ "\nYou entered Ltype=")(princ lt)
(princ)
)
How to call VBA/ActiveX functions from lisp
Ever want to change a setting in AutoCAD that is only changeable from the options dialog
box?
For example, set the current profile or turn the screen menu on from lisp. There's no system
variable for displaying the screen menu and the current profile can be viewed as a sysvar but it's
read-only so you can't set it. However, from VBA these things can be done quite easily. The
9
screen menu is control ed by a property on the preferences display object. The current profile is
control ed by a profiles object that can also be accessed through the preferences object.
The good news is that you can use the same methods and properties that are accessible in
VBA from LISP as wel .
Here are the basics:
o
Initialize COM support in lisp using (vla-load-com). This only needs to be done
one time per AutoCAD session.
o
Get the AutoCAD application object: (setq app (vlax-get-acad-object)).
Once you have the AutoCAD application object you can access it's properties and get
other sub-objects, such as a collection of open drawings, the preferences object or just
about anything you can access from VBA.
Once you have an object; there are three basics to manipulating it using lisp.
1. Getting the value of a property.
(vla-get-propertyName <object>)
or the more general version (vlax-get <object> "propertyname")
2. Setting a property value.
(vla-put-propertyname <object>)
or the more general version (vlax-put <object> "propertyname")
3. Invoking methods or functions on an object.
(vla-methodname <object> [arg1] [arg2] [arg3] ...)
or the more general version:
(vlax-invoke <object> "methodname" [arg1] [arg2] [arg3] ...)
Tips for getting VBA/ActiveX API information.
o
It is important to learn how to translate documentation in VBA form into lisp syntax.
For example; To get the preferences object in VBA the syntax is documented like this:
Application.Preferences
So in VBA you might write something like:
Dim pref as AcadPreferences
Set pref = Application.Preferences
In lisp you would write something like this...
(setq app (vlax-get-acad-object))
;; get the Application object
(setq pref (vla-get-preferences app))
Note the vla-get-propertyname syntax.
10
Add New Comment