Alisp
Alisp
Alisp
Welcome to the world of AutoLisp. Soon you will know what the hell a car of a cdr is.
When I say "type this at the command line" I will put the actual key strokes in red except for the enter key. This will mean
that you should type the red letters in at the Command Prompt inside AutoCAD as if you were typing the command "Line".
To clarify, If I say "type this at the command line: Line and press the enter key, you would type the word "Line" at the
command prompt inside of AutoCAD and press the enter key afterwards. Got it? No? Oh, never mind. Just keep reading. I
won't leave you behind.
(defun myProg()
(princ "Jeff")
(princ)
)
You can execute AutoLisp at the Command Prompt inside AutoCAD. This is where we will start.
AutoLisp is a language that returns a value after executing. Every function that executes will return or echo it's answer.
Let's test this. Open AutoCAD and type this at the command line: (+ 1 2) and press the enter key. (The + function adds
numbers together). Look at the command line. You should see something that looks like this:
Notice the 3 below the line you typed and the Command Prompt? AutoLisp returned the value of the function you typed in.
Try it again. This time change the 1 to 3 and the 2 to 5. As in (+ 3 5). What did it return? AutoLisp ALWAYS returns the
answer.
Variables
-1-
What the hell is a variable? Variables are names or placeholders if you will, for values. Remember algebra? (Your going
to wish you would have paid attention to Mrs. Johnson instead of that cheerleader on the first row with the blond hair and the
big...well ...nevermind.) X=1 Y=2. Look familiar? X and Y are variables. They could mean anything. If you need to store
data, such as a string or integer, you will need to assign a variable name to the data. The way to do this is to use the setq
function. Ex. (setq a 1). This set's the variable a to the value 1. Anytime you use the variable a in your program, it will
actually use the value 1. If you typed (+ a 2) and pressed enter, AutoLisp would return 3.
You can use any variable name you want. (Practically) Upper case and lower case does not matter. I would suggest you do
not use any names that AutoCAD has in use. How do you know which ones are being used by AutoCAD? Good question
Jeff. The simplest method to find out if AutoCAD is using a name is to type in the name at the Command Prompt inside of
AutoCAD and press the enter key. If nothing happens, use it.
If you were to write a program named Line.lsp and load the program, AutoCAD's built in LINE command will seem to be
replaced. When you type Line you will run your program instead of the Line command. You will get the built in functions
back next time you open a new drawing or you could use a period in front of the command to make it execute an AutoCAD
built in function. Ex. .LINE would execute the AutoCAD built in function instead of your program. I do not suggest toying
with this.
Name your variables and programs something unique. AutoCAD does not have any "built in" functions that are a single
character. So a through z are fair game. ( Unless they are assigned in the programs that automatically load during startup
such as the ACAD.lsp, ACADDOC.lsp, and ACAD.PGP files. Better check your variable names at the command line as
discussed earlier. Also, the letter T is used by autolisp as a TRUE symbol.)
[ Thanks Ethan! ]
(setq a 1)
(setq b1 3)
(setq aa1 4)
(setq jeffsVarForHisProgram 1)
(setq thisVarA 2)
(setq line 1)
(setq acad 4)
(setq ltscale 7)
I suggest you use variable names that are somewhat descriptive. If you had to name a variable for a string that contains
people's names then I would use something like this (setq strName "Jeff").
-2-
If you have a variable name for a integer that holds the data for a value that represents a title block selection then I would
use something like this (setq intTBSelection 3).
While inside AutoCAD you can check your variables to see what they are set to. This is very handy when debugging a
program. Type (setq aa1 454) and press enter. To check the value of the variable we just declared, simply type !aa1 at
the command prompt. Go ahead. Try it. AutoLisp echo's the value to the command line. Using an exclamation point in
front of the variable prints the value of the variable to the command line.
New! Have you ever seen an AutoLisp program crash? Ever try to debug it? It looks like a bunch of garbled mess. How
in the heck can you cipher through all of this:
It's not as tough as it looks. You only have to worry about the first or second lines returned. In this case the culprit that
crashed the program is (+ CANT 1). Let's look at the variable CANT. At the command prompt type !CANT<enter> It will
return nil. You can't add nil to 1. Why is CANT set to nil? Let's look at the next line. (SETQ CNT(+ CANT 1)) This
looks like a counter of some type. I'll bet the variable name CANT should have been CNT. A typo! At this point I would
open the program with NotePad and search for the text string CANT. If this is the only place it shows up then my first
explanation would be correct. I would retype the CANT variable to be CNT. Save the program and try it again.
Functions
Let's take it a step further and create our very own function. Every defined function begins with the declaration defun.
The difference between using the C: and not using the C: will be explained later. For now. let's learn another AutoLisp
function to use in our first program. The function princ simply prints to the command line. Ex. (princ "Jeff") would print Jeff
on the command line. Alrighty then, let's type this at the command prompt:
-3-
Now type (myprog) at the command prompt and press the enter key. You should see something that looks like this:
The program executed and printed "Jeff" on the command line. Then the program echoed the last statement to the
command line. AutoLisp always returns the answer right? That is why you ended up with Jeff"Jeff" on the command line.
The way to get rid of the echo is to use a princ statement without any parameters as the last statement in your program. Ex.
(princ).
Let's rewrite the program using the (princ) function to stop the echo. Type this is in at the command prompt:
Then type (myprog) and press enter. What happened? No echo. Cool.
Let's back track and explain the C: we mentioned earlier. The C: tells AutoCAD that you want this program to be executed
at the Command Prompt like a built in function. Let's redo our program. Type this at the command line:
Now type (myprog) and press enter. What happened? Why did it print Jeff? We used the C: in front of the function. We
do not have to put the program inside of quotes to execute it. (There are some other differences, we will get to that in the
advanced levels.) To execute a function that was declared with a C: you only have to type the name of the program at the
command prompt. Let's do that now. Type myProg at the command prompt and press enter. It printed "Sanders" to the
command line. Wow! Don't rush off to show everyone yet. The next time you open a drawing your new function will
disappear and you will have to type it in again. Dang!
Wait! We could save it to disk and reload it whenever we needed it. Let's do that. I will assume you have a windows
operating system for the following instructions. Yes. I know what happens when you assume something. Let's not go there.
(defun C:myProg()
(princ "Jeff")
(princ "Sanders")
(princ "AutoLisp")
(princ)
)
-4-
Now, go to the "File" drop down menu in NotePad and select "SaveAs". Type in myProg.lsp for the name of the program.
Wait! Don't hit the SAVE button yet. NotePad has a bad habit of assuming you want to save it as a text file with the
extension txt. Go to the "SAVE AS TYPE" drop down box and select "ALL FILES". Look at the top of NotePad in the "Save
in:" location. Make sure you save the file in a location where AutoCAD can find your file in it's search path. I suggest
creating a directory on your hard drive to keep all of your AutoLisp files in. I would suggest a directory such as :
"C:\ACAD\lsp". Change the "Save in: " location to point to this directory. Now press the SAVE button. Go back to
AutoCAD. Go to the "Tools" drop down menu and go to "Preferences". Click the "Files" tab. Click the + sign in front of
"Support File Search Path". Click the "ADD" button. Type in the directory you saved the "myProg" lisp file in. Ex.
"C:\ACAD\lsp" . Click the "APPLY" button. Click OK to the alert box. Click the OK button to exit the dialog box.
At the command prompt inside AutoCAD type (load "myProg") and press enter. Then type myprog and press enter. If
you hit the F2 button to bring up the text screen, you should see something that looks like this:
You have successfully created an AutoLisp program. Congratulations! One last thing about functions:
We discussed the defun() statement and what it meant. We did not discuss the open and close parenthesis "()" after the defun
statement. We will do that in the intermediate tutorial level. The point I wanted to get to here is, if you put your variable
names inside these parenthesis and after a / you will reset these variables to nothing...or nil as AutoLisp defines it. Therefore
the exclamation point before the variable name will return nil. This saves room in RAM and also keeps your program from
getting corrupted by other programs that do not reset the variables to nil. Always include the variables you use inside the
parenthesis after debugging your program.
You are so, so, close to uncovering the mysteries surrounding the car and cdr quandaries. Move on to the next level.
LEVEL 2
Welcome to the Intermediate Tutorial. You know the beginners steps to writing an AutoLisp program. You don't? See the
Beginner's Tutorial. Now all you need to know is what to write. What functions are available? How do you manipulate
entities inside of AutoCAD? What the heck is a car of a cdr?
I believe that you not only need to know what a function returns if it is successful, but also what the function will return if it
fails. This will help you troubleshoot your programs. That is why I believe this will become the Ultimate AutoLisp Tutorial.
In the examples of the functions below, I've included the most common reasons why AutoLisp programs crash along with the
error message you will receive. Remember, when AutoLisp has an error, execution is aborted.
-5-
Let's begin with Data Types:
Data Types:
String
Integers
Real
List
A list is an AutoLisp data type that has parenthesis around it. Multiple data types can reside inside one list. This will also
unravel the mystery behind the car and the cdr.
Variables or Symbols
We covered these in the Beginner's Tutorial. A variable is a symbol that is created by you, the user. eg. (setq a 1) (setq
pt1(getpoint "\n Select Point: ")) - a and pt1 are the variables.
Associated List
An associated list is a list that has a value associated with it. The assocation and the data are seperated by a period. eg. (1 .
455) (34 . "Sanders") You can find "Sanders" by looking up group code 34. We will get more into this when we take a look
at the DXF Group Codes later in this Tutorial.
Let's get started with the basic functions. Remember, you don't need to know all of this. You don't need to memorize all of
this. This is a reference. There will be no test. It will help you to read through it once to see what functions are available.
When you are writing a program and get stuck, come back here to find the correct function to solve your problem. Put down
your pencil. Really.... no test.
-6-
List Functions car cdr cadr caddr caar cddr foreach list cons nth
String Functions
Functions - substr strcase strlen strcat Example Programs
"string" - any valid string or variable representing a string. If you use a variable name omit the quotes.
startPoint - an integer or variable representing an integer that corresponds to the position in the stringto
start the substring at. The first character is character 1.
Returns a partial string starting at the character represented by startPoint and ending at the character
represented by the numberOfCharacters or the end of the string. Whichever comes first..
-7-
(substr "Jeff Sanders" 64 456) would return ""
(substr "Jeff Sanders" -4 3) would return "Error: Bad argument" [No negative character
positions]
(substr "Jeff Sanders" 4 -3) would return "Error: Bad argument" [No negative string lengths]
If you omit the last parameter, the remainder of the string from the startPoint will be returned..
"string" - any valid string or variable representing a string. If you use a variable omit the quotes.
flag = T or nil
Returns a string that is either upper or lower case depending on the flag setting.
(strcase 123 T) returns "Error: Bad Argument Type" [123 is an integer, not a string]
(strcase 1.0 nil) returns "Error: Bad Argument Type" [1.0 is a real number, not a string]
Omitting the flag is the same as having the flag set to nil or false.
-8-
strlen - This function returns the length of a string in characters.
"string" - any valid string or variable representing a string. If you use a variable omit the quotes.
Returns an integer that represents the length of the string. The length is the number of characters.
(strlen 123) returns "Error: Bad Argument Type [123 is an integer, not a string]
(strlen 1.0) returns "Error: Bad Argument Type [1.0 is a real number, not a string]
(strlen (list 1.0 2.0)) returns "Error: Bad Argument Type [(list 1.0 2.0) is a list, not a string]
strcat - This function adds multiple strings together to form one string.
"string1" - any valid string or variable representing a string. If you use a variable omit the quotes.
(strcat "" 5 "Jeff") returns "Error: Bad argument type". [5 is an integer, not a string]
(strcat "" 3.1 "Jeff") returns "Error: Bad argument type". [3.1 is a real number, not a string]
This function can only accept strings or a variable representing a string as it's arguments. (parameters)
Example Program 1:
(defun C:myProg()
-9-
(setq str2 "P")
(setq str5 (strcat str4 " " str2)) ;sets str5 to "JEFF P"
Command: myProg<enter>
Command: JeffJEFF4JEFF P
Program Example 2:
(defun C:NameMix()
(setq keepGoing 1)
- 10 -
(princ "\n Program Complete.")
(princ)
Command: namemix<enter>
Command: Jeff
Command: JEff
Command: JEFf
Command: JEFF
Number Functions
abs atof atoi fix float itoa Example Program
(abs "345JEFF") returns "Error: Bad Argument Type" ["345JEFF" is a string, not a number]
- 11 -
Syntax : (atoi "string")
"string" - any valid string or variable representing a string. If you use a variable omit the quotes.
Returns an integer.
(atoi 5.6) returns "Error: Bad argument type". [5.6 is a real number, not a string]
This function looks at the first character, then the next, then the next, ect. until it finds a character that
cannot be part of an integer.
Returns a string.
(itoa 5.6) returns "Error: Bad argument type". [5.6 is a real number, not an integer]
(itoa "5") returns "Error: Bad argument type". ["5" is a string not an integer]
(itoa "345JEFF") returns "Error: Bad argument type". ["345JEFF" is a string, not an integer]
This function can only accept an integer or a variable representing an integer as it's parameter.
"string" - any valid string or variable representing a string. If you use a variable omit the quotes.
- 12 -
(atof "Jeff345") returns 0.0
(atof 345) returns "Error: Bad Argument Type" [345 is an integer, not a string]
(atof 3.4) returns "Error: Bad Argument Type" [345 is a real number, not a string]
(atof (list 3 4)) returns "Error: Bad Argument Type" [(list 3 4) is a list, not a string]
This function looks at the first character, then the next, then the next, ect. until it finds a character that
cannot be part of an real number.
Returns an integer.
(fix "5.0") returns "Error: Bad Argument Type ["5.0" is a string, not a real number]
(fix "5") returns "Error: Bad Argument Type ["5" is a string, not a real number]
This function takes the whole number part of a decimal number and returns it as an integer.
float - This function takes a number (integer or real) and converts it to a real number.
- 13 -
real - Any valid real number.
(float "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(float "3.5") returns "Error: Bad Argument Type" ["3.5" is a string, not a number]
(float "abc") returns "Error: Bad Argument Type" ["abc" is a string, not a number]
Example Program 1:
(defun C:myProg()
(princ (strcat "\n You are " (itoa intDays) " Days old!"))
(princ)
Execution:
Command: myProg<enter>
List Functions :
car cdr cadr caddr caar cddr foreach list cons nth
Example Programs
You are about to unravel the mystery behind the car of a cdr. Better sit down.
- 14 -
car - This function returns the first item of a list. The item can be a list as shown in example 3 below.
(car "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
This function is mainly used to get the x coordinate of a point. [ (car (x y z)) returns x ]
cdr - This function returns a list that includes everything but the first item in a list. The item can be a list as shown in
example 3 below.
(cdr ((1 2 3) (4 5 6))) returns (4 5 6) since (4 5 6) is the second item in the list.
(cdr "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
Mystery HINT: (car (cdr ( 1 (2 3) ) ) ) returns 2 [So, that's what a car of a cdr is]
if the cdr of (1 (2 3)) returns (2 3) and the car of (2 3) returns 2 then the mystery is solved.
The car of a cdr is the first item in the second item in the list.
- 15 -
cadr - This function returns the second item in a list. The item can be a list as shown in example 3 below.
(cadr "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
This function is mainly used to get the y coordinate of a point. [ (cadr (x y z)) returns y ]
caddr - This function returns the third item in a list. The item can be a list as shown in example 3 below.
(caddr "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
This function is mainly used to get the z coordinate of a point. [ (caddr (x y z)) returns z ]
caar - This function returns the first item of the first item in a list. The item can be a list as shown in example 3
below.
- 16 -
Syntax : (caar list)
(caar (1 2 3)) returns "Error: Bad Argument Type" [The first item in the list is not a list]
(caar "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
(caar (1 (2 3))) returns "Error: Bad Argument Type" [The first item in the list is not a list]
cddr - This function returns a list that includes everything after the second item in a list.
(cddr "Jeff") returns "Error: Bad Argument Type" ["Jeff" is a string, not a list]
foreach - This function steps through each item in the list and returns the last value.
- 17 -
(foreach a (list 1 2 3)(princ a)) prints 123 to the screen and returns 3 [same as
(princ 1) (princ 2) (princ 3) except that it only returns the last value.]
(foreach a (list 1 2 3)(princ (+ a 5))) prints 678 to the screen and returns 8
[same as (princ (+ 1 5)) (princ (+ 2 5)) (princ (+ 3 5)) except that it only returns the last value.]
(list 1 2 3) returns (1 2 3)
(list "ab" "cde" "Jeff" "Sanders") returns ("ab" "cde" "Jeff" "Sanders")
cons - This function takes an item and a list, and returns the addition of that item to the beginning of the list. The first item
can be an atom or a list.
(cons) returns "Error: Too Few Arguments" [cons requires an item and a list]
(cons 1 2 3) returns "Error: Too Many Arguments" [cons requires an item and a list]
(cons "ab" (list "cde" "Jeff" "Sanders")) returns ("ab" "cde" "Jeff" "Sanders")
- 18 -
(cons 1) returns "Error: Too Few Arguments" [cons requires an item and a list]
(cons "Jeff") returns "Error: Too Few Arguments" [cons requires an item and a list]
(cons 1 (list 2 3)) returns (1 2 3) [notice the difference here from the list function above]
nth - This function returns the Nth item in a list. The first item in a list is item zero.
(nth) returns "Error: Too Few Arguments" [cons requires an item and a list]
(nth 2 1 2 3) returns "Error: Bad Argument Type" [nth requires an integer and a list]
(nth 1 (list "a" (list "b" "c") "d")) returns ("b" "c")
Example Program 1:
(setq pt2(getcorner pt1 "\n Last Corner: ")) [get other point on a square]
- 19 -
(setq y2(cadr pt2)) [get y coordinate of pt2]
) [close program]
Execution:
Command: myProg<enter>
Example Program 2:
(setq myList(list "e" "b" "c" "m" "at")) [make a list of 6 letters]
) [close program]
Execution:
- 20 -
Command: AddText<enter>
Command: eat
Command: bat
Command: cat
Command: mat
Command: atat
All entities inside an AutoCAD drawing has DXF Group codes. The group codes define the properties of each entity. Each
code inside the group is acutally an associtated list. The group codes for a line may look something like this:
Where:
The group codes for a circle may look something like this:
Where:
- 21 -
Group code 0 is the type of entity.
How do you list the codes for an entity? Type this at the command line, press enter, and select an entity.
(entget(car(entsel)))
Entsel selects the entity, car gets the entity name from entsel, and entget returns the Group Codes of the entity.
What are the numbers in the group code for? They aide in extracting data from the group codes. Take for instance this
code:
If I selected a circle, this code would set entList to something like this:
If I wanted to extract data from this group code list I would simply type:
Conversion Functions:
fix float itoa atoi atof rtos angtos
fix - This function returns an integer. (NOTE: This function does not round numbers off!)
- 22 -
number - any valid number.[integer or real number]
(fix "345JEFF") returns "Error: Bad Argument Type" ["345JEFF" is a string, not a number]
(float "345JEFF") returns "Error: Bad Argument Type" ["345JEFF" is a string, not a number]
(itoa 345.9) returns "Error: Bad Argument Type" [345.9 is a real number, not an integer]
(itoa -345.9) returns "Error: Bad Argument Type" [-345.9 is a real number, not an integer]
(itoa "345JEFF") returns "Error: Bad Argument Type" ["345JEFF" is a string, not an integer]
- 23 -
Syntax : (atoi string)
(atoi 345) returns "Error: Bad Argument Type" [345 is a number, not a string]
(atoi 345.9) returns "Error: Bad Argument Type" [345.9 is a number, not a string]
Note: This function starts at the beginning of the string and continues until it finds a character that cannot be an integer.
(atof 345) returns "Error: Bad Argument Type" [345 is a number, not a string]
(atof 345.9) returns "Error: Bad Argument Type" [345.9 is a number, not a string]
Note: This function starts at the beginning of the string and continues until it finds a character that cannot be a number.
- 24 -
Syntax : (rtos number mode precision)
mode = 1 = Scientific
ect.....
mode = 2 = Engineering
ect.....
mode = 3 = Decimal
- 25 -
with precision = 4 (rtos 456.5 3 4) returns "38'-0.5000""
ect.....
mode = 4 = Architectural
ect.....
ect.....
- 26 -
number - Any valid number representing radians. [3.14159 radians = pi =180 degrees]
ect.....
ect.....
mode = 2 = Grads
ect.....
mode = 3 = Radians
- 27 -
with precision = 2 (angtos 0.627102 3 2) returns "0.63r"
ect.....
mode = 4 = Surveyor
ect.....
Math Functions
+ - Addition.
(+ 3 4) returns 7
(+ 3 4 5) returns 12
(+ "3" "4") returns "Error: Bad Argument Type" ["3" and "4" are strings, not numbers]
- 28 -
- - Subtraction.
(- 4 3) returns 1
(- 9 5 2) returns 2
(- "3" "4") returns "Error: Bad Argument Type" ["3" and "4" are strings, not numbers]
/ - Division.
(/ 9 3) returns 3
(/ 5 2.0) returns 2.5 [if either number is a real number then it returns a real number]
(/ 5.0 2) returns 2.5 [if either number is a real number then it returns a real number]
(/ "3" "4") returns "Error: Bad Argument Type" ["3" and "4" are strings, not numbers]
* - Multiplication.
- 29 -
Returns an integer or a real number.
(* 9 3) returns 12
(* 5 2.0) returns 10.0 [if either number is a real number then it returns a real number]
(* 5.0 2) returns 10.0 [if either number is a real number then it returns a real number]
(* "3" "4") returns "Error: Bad Argument Type" ["3" and "4" are strings, not numbers]
(1+ 3) returns 4
(1+ 5.0) returns 6.0 [if number is a real number then it returns a real number]
(1+ "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(1+ 3 4) returns "Error: Too Many Arguments" [Only accepts one number as argument]
(1- 3) returns 2
(1- 5.0) returns 4.0 [if number is a real number then it returns a real number]
(1- "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(1- 3 4) returns "Error: Too Many Arguments" [Only accepts one number as argument]
- 30 -
cos - Returns the cosine of an angle expressed in radians.
(Note: Radians are AutoCads angular units. A circle = 2*pi or 180 degrees = pi )
number - any valid number.[integer or real number] that represents an angle expressed in radians.
(cos "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(cos 3 4) returns "Error: Too Many Arguments" [Only accepts one number as argument]
(Note: Radians are AutoCads angular units. A circle = 2*pi or 180 degrees = pi )
number2 - any valid number.[integer or real number]. This is optional and is usually omitted.
(atan "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
- 31 -
(Note: Radians are AutoCads angular units. A circle = 2*pi or 180 degrees = pi )
number - any valid number.[integer or real number] that represents an angle expressed in radians.
(sin "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(sin 3 4) returns "Error: Too Many Arguments" [Only accepts one number as an argument]
(sqrt "3") returns "Error: Bad Argument Type" ["3" is a string, not a number]
(sqrt 3 4) returns "Error: Too Many Arguments" [Only accepts one number as argument]
Returns a real number unless both number and power are integers, then it returns an integer..
(expt 2 2) returns 4
- 32 -
(expt 2 2.0) returns 4.0
(expt 5 2) returns 25
(expt "3" 2) returns "Error: Bad Argument Type" ["3" is a string, not a number]
Example Program 1:
(defun C:myProg()
(princ (strcat "\n You are " (itoa intDays) " Days old!"))
(princ (strcat "\n You are " (itoa intWeeks) " Weeks old!"))
(princ)
Execution:
Command: myProg<enter>
Example Program 2:
(defun C:Triangle()
- 33 -
(setq y1(cadr pt1)) ;y-coord of point one
(princ)
Execution:
Command: Triangle<enter>
Command:
Selecting Entities
Functions - entsel ssget Example Programs
entsel - This function prompts for the user to pick one entity.
Syntax : (entsel)
(car(entsel)) returns the entity name if entsel does not return nil..
(cadr(entsel)) returns the point selected if entsel does not return nil..
- 34 -
Note: Selecting an area in the drawing where there are no entities would return nil. You will get an error
if you try to get the car or cadr of nil as shown above. The best thing to do is to save the return value of the entsel
function, then check to see if it is a valid selection.
Example:
(if myEnt ; if the value of myEnt is not nil then do some stuff
Syntax : (ssget mode) ;prompts the user for a selection set and sets mode to "W", "C", "L", and "P",
corresponding to the Window, Crossing, Last, and Previous selection methods. Another
optional mode value is "X", which selects the entire database. There are other modes, but this
list contains the modes that will be used most often.
Syntax : (ssget "W" ) ;prompts the user for a selection set using the Window mode.
Syntax : (ssget "C" ) ;prompts the user for a selection set using the Crossing mode.
filter_List - an association list containing valid filters for entities corresponding to the Entity DXF Group
Codes. What did I just say? Entity DXF Group Codes? What the heck is that?
Click here to find out.
You can filter out enitites that do no match your criteria using the filter_List. If you only
wanted entities that were on layer "STR" then you could add a filter list that looks like this:
- 35 -
(ssget "X" myFilter) ;returns a selection set containing only entities on layer "STR"
or
Example Program 1:
(defun C:myProg()
Execution:
Command: myProg<enter>
- 36 -
Command: Displacement Point: <pick>
Command:
Example Program 2:
(defun C:myProg2()
(if (setq mySet(ssget "X" (list (cons 8 "STR")(cons 0 "CIRCLE")))) ;get set
(alert "No entites Match Criteria!") ;else alert the user of an error
Execution:
Command: myProg2<enter>
Command:
ssadd - This function does one of three things depending on how many arguments you send with it.
If you use the ssadd function with no arguments it will return a new selection set with no members.
- 37 -
Example : (setq myNewSet(ssadd))
If you use ssadd with a name of an entity it will return a new selection set with only that one member.
If you use ssadd with a name of an entity and name of an selection set, it will add that entity to the set.
And return the selection set with the entity added to the set.
Returns the selection set with the new entity added to it.
Example: Let's build a function that allows me to add items to a selection set until I stop.
(while(/= (setq enS(entsel)) nil) ;loop until I pick nothing or press enter.
(setq en(car enS)) ;get the entity name of the selected item
Command: move<enter>
Command: !mySet<enter> ;don't forget the exclamation point in front of the variable name.
Did everything get selected to be moved? Cool. Move individual items around and try it again. Did everything get
selected again? Cool. Why? Because AutoLisp finds them by their entity name, not their location.
ssdel - This function does the opposite of ssadd. SSDEL removes an entity from a selection set.
- 38 -
Syntax : (ssdel entityName selectionSetName)
Assuming en3 is a valid entity name but is not part of any selection set.
Note: If you filter correctly using the ssget "X" function, you shouldn't need ssdel.
sslength - This function returns the number of entities inside a selection set.
Returns a real number if the number is greater than 32,767, otherwise it returns an integer.
The number, real or integer, represents the quantity of items inside the selection set.
- 39 -
(sslength ss3) ;returns 0 as an integer
ssname - This function returns an entity name contained inside a selection set.
Note: nthItem needs to be a real if the set contains more than 32,767 items.
(ssname ss2 34555) ;returns ERROR - Bad Argument Type (needs real)
Example Program 1:
I will write a program that prints the layer name of every entity in a selection set.
- 40 -
(progn ;use progn since there will be more than 1 statement
(setq cntr 0) ;set the cntr to the first item in the set
(while (< cntr (sslength eset)) ;while cntr is less than the length of the set
;note: the length is one more than the index of items since the first item is zero. In other words, to get to the ;
first item in a selection set consisting of one item you would use (ssname eset 0) not (ssname eset 1).
(setq en(ssname eset cntr)) ;get the entity name of the item indexed with cntr
(setq enlist(entget en)) ;get the dxf group codes of the enitity
(princ "\n ") ;print "\n " will cause a new line to be printed
(princ "\n Error - No entities selected.") ;print a message on the else statement
; note: the if statement can be a " if then " statement or a " if then else" statement
Note: I personally do not find any of these functions useful except for the ssname and sslength functions.
If you use your ssget "X" filters well enough there is no reason for the ssadd or ssdel statements.
There are other selection set functions that are even less useful such as ssmemb, sssetfirst, & ssgetfirst.
When you loop through your selection set you can check an entities DXF group codes and decide whether the entity is
one to either skip or work on. So, you can avoid all of these useless functions. Don't get me wrong. I'm not saying they
are completely useless and you shouldn't use them. I'm saying why bother.
Entity Functions
entget entlast entnext entdel entmod entupd Example Program
- 41 -
entget - This function returns the DXF Group Codes of the entity.
ename - a valid entity name returned by entsel, ssname, entlast, or the entnext function.
((-1 . <Entity name: 3b40a60>) (0 . "LINE") (5 . "FC") (100 . "AcDbEntity") (67 . 0) (8 . "DIM")
(100 . "AcDbLine") (10 252.578 68.25 0.0) (11 188.379 31.5 0.0) (210 0.0 0.0 1.0))
entlast - This function returns the name of the last non-deleted entity in the drawing.
Syntax : (entlast)
This function is mainly used to get the last entity added to the drawing.
entnext - Let's look at AutoDesk's explanation: If entnext is called with no arguments, it returns the entity name
of the first nondeleted entity in the database. If entnext is called with an entity name argument ename, it
returns the entity name of the first nondeleted entity following ename in the database. If there is no next
entity in the database, it returns nil. The entnext function returns both main entities and subentities.
The entities selected by ssget are main entities, not attributes of blocks or vertices of polylines. You can
access the internal structure of these complex entities by walking through the subentities with entnext.
Once you obtain a subentity's name, you can operate on it like any other entity. If you obtain the name
of a subentity with entnext, you can find the parent entity by walking forward with entnext until a seqend
entity is found, then extracting the -2 group from that entity, which is the main entity's name.
ename - a valid entity name returned by entsel, ssname, entlast, or the entnext function.
(entnext ename) returns the name of the next entity following ename.
- 42 -
entdel - This function deletes an entity from the drawing if it exist. If it does not exist and has been previously
deleted, it will undelete the entity.
ename - a valid entity name returned by entsel, ssname, entlast, or the entnext function.
(entdel en) deletes the entity and returns the name of the deleted entity.
(entdel en) undeletes the entity and returns the name of the undeleted entity.
elist - a list that is returned from an entget statement. (DXF Group Codes of an entity)
(setq elist(entget en)) returns the DXF Group Codes of the entity.
The line above Replaces existing layer name in the elist with new layer name "DIM"
(entmod elist) updates the AutoCAD database and replaces the old layer name with the new one.
ename - a valid entity name returned by entsel, ssname, entlast, or the entnext function.
Note: If you modify the DXF Group Codes of an entity, such as changing the layer (8 . "LAyerName"), you
will not notice the change until you regenerate the entire drawing or use entupd to regenerate the entity.
- 43 -
End of Entity Functions
Example Program 1:
(defun C:CLay()
(if(setq ent(entsel))
(progn
(entmod enlist)
(entupd en)
(princ)
Execution:
Command: CLay<enter>
Command:
- 44 -
open/close - Open a file and close a file. That simple.
"w" - Write mode. Writes to a file with either write-char, write-line, princ, or print.
"a" - Append mode. Appends to an existing file using write-char, write-line, princ, or print.
Note: If the existing file is not found in append mode, autolisp will create a new empty file.
Note: You must use (setq varNam(open "file" mode)) to enable closing of the file.
Note: Writing the file does not occur until you use the close statement.
.
Example:
(progn
(princ txtLine)
(close f)
Where fileVar is a valid variable name that represents an open file descriptor.
- 45 -
Assuming (setq f(open "text.txt" "r")) returned sucessfully.
Where fileVar is a valid variable name that represents an open file descriptor.
(write-line "Hello World" f) ;writes text to file and returns string "Hello World"
Final note:
I've stated over and over that the OPEN statement opens a file. This can also be any resource available to you. Instead of
opening "C:\ACAD\MyTextFile.txt" you can just as easily open "LPT1" or "\\myserver\myprinter" and write the file to a
printer exactly like you write to a file. Cool! Printing from AutoLisp!
One thing to keep in mind. The file does not get written to disk or sent to the printer until after the CLOSE statement. [ This
will help you when debugging.] If you cannot get a printer to print, write the data into a text file instead of the printer. Then
open the file with notepad and see if it is what you expect. If the data looks good in notepad then you've probably got the
printer name wrong, a printer problem, or a network access problem.
Example Program 1:
Let's write a program that prints a text file on the command line.
(setq fname(getstring "\n Enter a valid file name: ")) ;get file name cheaply
- 46 -
(if(setq f(open fname "r")) ;open the file in read mode
(princ "\n Error - Could not find file") ;print a message on the else statement
; note: the if statement can be a " if then " statement or a " if then else" statement
Entity Name
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
(Primary)
Entity Name
-2 -2
(Secondary)
Primary Text
1 1 1 1 1
Value
Name: Shape,
2 2 2 2 2 2
Block, Tag
3 Prompt String 3
- 47 -
Handle
5 (Hexadecimal 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
String)
6 LineType Name 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
7 Text Style Name 7 7 7
8 Layer Name 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
X of Start or Insert
10 10 10 10 10 10 10 10
Point
X of Center Point 10 10
X of Corner Point 10 10 10
10
X of Definition
10
Point
X of Elev. Point
10
(2D Poly)
X of End or
11
Insertion Point
X of Corner Point 11 11 11
11 X of Alignment
11 11 11
Point
X of Middle Point
11
of Dimension
X of Corner Point 12 12 12
12 X of Insertion
12
Point
X of Corner Point 13 13 13
13 X of Definition
13
Point
- 48 -
X of Definition
14 14
Point
X of Definition
15 15
Point
X of Definition
16 16
Point
Y of Start or
20 20 20 20 20 20 20 20
Insertion Point
Y of Center Point 20 20
Y of Corner Point 20 20 20
20
Y of Definition
20
Point
Y of Elev. Point
20
(2D Poly)
Y of End or Insert
21
Point
Y of Corner Point 21 21 21
21 Y of Alignment
21 21 21
Point
Y of Middle Point
21
of Dimension
- 49 -
CODE DESCRIPTION LN PT CI AR TR SD TX SH IN AD AT PL VT DM 3DF SQ
Y of Corner Point 22 22 22
22
Y of Insert Point 22
Y of Corner Point 23 23 23
23 Y of Definition
23
Point
Y of Definition
24 24
Point
Y of Definition
25 25
Point
Y of Definition
26 26
Point
Z of Start or Insert
30 30 30 30 30 30 30 30
Point
Z of Center Point 30 30
Z of Corner Point 30 30 30
30
Z of Definition
30
Point
Z of Elevation
30
Point (2D Poly)
Z of End Point 31
Z of Corner Point 31 31 31
31 Z of Alignment
31 31 31
Point
Z of Middle Point
31
of Dimension
32 Z of Corner Point 32 32 32
- 50 -
Z of Insert Point 32
Z of Corner Point 33 33 33
33 Z of Definition
33
Point
Z of Definition
34 34
Point
Z of Definition
35 35
Point
Z of Definition
36 36
Point
38 Entity Elevation 38 38 38 38 38 38 38 38 38 38 38 38 38 38
39 Entity Thickness 39 39 39 39 39 39 39 39 39 39 39 39 39 39
Radius 40 40
Height, Size, or
40 40 40 40 40 40
Width
Leader Length 40
X Scale Factor or
41 41 41 41 41 41 41
Width
Y Scale Factor or
42 42 42
Bulge
43 Z Scale Factor 43
44 Column Spacing 44
45 Row Spacing 45
Rotation Angle 50 50 50 50 50 50 50
50 Start Angle 50
Curve Fit Tangent 50
End Angle 51
Obliquing Angle 51 51 51 51
51
Angle from
51
Horizontal
62 Color 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62
Entities Follow
66 66 66
Flag
70 Dimension Type 70
Vertex or Polyline
70 70
Flag
Attribute Flag 70 70
Column Count 70
- 51 -
Invisible Edges
70
Flag
Text Generation
71 71 71
Flag
71 Row Count 71
Mesh M Vertex
71
Count
Text Justification 72 72 72
72 Mesh N Vertex
72
Count
Field Length 73 73
73 Smooth Surface M
73
Density
Smooth Surface N
74 74
Denstiy
Smooth Surface
75 75
Type
X of Extrusion
210 210 210 210 210 210 210 210 210 210 210 210 210 210
Point
Y of Extrusion
220 210 210 210 210 210 210 210 210 210 210 210 210 210
Point
Z of Extrusion
230 210 210 210 210 210 210 210 210 210 210 210 210 210
Point
The idea for this table was from the book "Inside AutoLisp - Using AutoLisp to Customize AutoCAD" by Joseph Smith and
Rusty Gesner.
- 52 -
The AutoLisp Advanced Tutorial
1/20/03 - Currently working on this. Per visitors request I'll continue where I left off from the Intermediate Tutorials. What
was I thinking?
Let's get started with the Advanced Tutorial. But where? Where do we start? I can't decide. I'm stuck between doing this
and doing that. Back and forth. Back and forth. Hardwired in an unconditional loop. Loop? Aha!
- 53 -
Conditional - cond if
Entities DXF Codes - Line Circle Text Arc PolyLine LWPolyline SPline
Ellipse Solid Trace Block Attribute
Loop Functions :
While Repeat
Example Programs
While
expression - Any valid autolisp expression that evaluates to true or non nil.
(while T (princ 1)) returns 1 and keeps returning 1 forever. Your locked into the loop.
----------------------------------------------------------
----------------------------------------------------------
(while (< cntr (sslength eset)) loop through all entities in the selection set
(setq en(ssname eset cntr)) get the entity name of the nth item
- 54 -
(setq enlist(entget en)) get the DXF group codes of the entity
(princ (cdr(assoc 0 enlist))) print the entity type to the command line
repeat - This function does exactly what you think it would do.
(repeat 20
(princ "A")
----------------------------------------------------------------------------------------
Example Program 1:
- 55 -
(while (/= nil (setq pt2 (getpoint pt1 "\n Next Point: "))) [get next point]
Example Program 2:
(while (/= myAnswer yourGuess) while you don't know the answer
(setq yourGuess(getstring "\n Guess what letter: ")) get your guess
Example Program 3:
(setq str(getstring "\n Enter a string:")) get a string from the user
(princ (strcat "\n " myChar)) print a new line then the nth character
- 56 -
Conditional Statements:
if cond
Coming soon...
thisIsTrue - Any valid autolisp expression that evaluates to true or non nil.
progn - Simply means there will be more than one statement here.
(if nil (princ 3)(princ 4)) returns 4 because nil always evaluates to False
(if (= nil T) (princ 3)(princ 4)) returns 4 because nil does not equal T
- 57 -
--------------------------------------------------------------------------------------------------
(if (= 3 3)
(progn
(princ 3) returns 3
(princ 5) returns 5
---------------------------------------------------------------------------------------------------
(if (= 3 3)
(progn
(princ 3) returns 3
(princ 5) returns 5
(progn
--------------------------------------------------------------------------------------------------
(if (= 3 4)
(progn
(princ 3) program never gets inside here because 3 does not equal 4
(princ 5) program never gets inside here because 3 does not equal 4
(progn
(princ 8) prints 8
- 58 -
(princ 9) prints 9
cond - This function test conditions until one of them is true. At that point it exits the function.
Syntax : (cond
(ifThisIsTrue thenDoThis)
(elseIfThisIsTrue thenDoThis)
(elseIfThisIsTrue thenDoThis)
Syntax : (cond
(elseIfThisIsTrue thenDoThis)
(elseIfthisIsTrue thenDoThis))
(elseIfthisIsTrue thenDoThis))
thisIsTrue - Any valid autolisp expression that evaluates to true or non nil.
progn - Simply means there will be more than one statement here.
- 59 -
(cond
) returns True
--------------------------------------------------------------------------------------------------
(cond
) returns True
--------------------------------------------------------------------------------------------------
(cond
) returns nil
--------------------------------------------------------------------------------------------------
(cond
) returns "Nothing"
--------------------------------------------------------------------------------------------------
- 60 -
(cond
) returns "Nothing"
Note: A good working example of all of these DXF group codes can be found in the CAD2File.lsp program on the AutoLisp
home page. All code is remarked to make it easier to follow along.
Arc
Typical DXF Group Codes for an ARC Entity:
To list an arc entity in AutoCAD you type LIST<enter> and select the arc. To do this in AutoLisp you would:
- 61 -
Would return:
(-1 . <Entity name:2b80648>) -1 - Entity Name (AutoCAD Automatically takes care of this)
(100 . "AcDbArc") 100 - Don't worry about this Sub Class Marker
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Factor
- 62 -
(cdr (assoc 10 enlist))
(cdr(assoc 40 enlist))
(cdr(assoc 8 enlist))
(cdr(assoc 50 enlist))
(cdr(assoc 51 enlist))
Attribute
This may be a little lengthy. Better grab a cup of joe before getting started.
First off, an attribute is located inside a block entity. This means the entities contained inside a block are called sub-
entities. All entities (Lines, text, attributes, etc.) inside the block are sub-entities. You have to (I'll retract this later) start with
the block entity and step your way into it to find the sub-entities. Kind of like this :
(block entity name (sub-entity name 1) (sub-entity name 2) (sub-entity name 3) SEQEND )
You start with the Block's entity name and step through until you find SEQEND using the ENTNEXT function. I'll get back
to this a little later on.
You have a couple of choices on how to get to the attribute entity inside the block. Now, after I've gone on the record saying
"You have to start at the top and step your way down through the sub-entities using ENTNEXT", I'm going to prove myself
wrong.
The easiest method is using NENTSEL. I have not discussed the NENTSEL function until now. NENTSEL will return the
DXF group code list for a sub-entity inside a block or 3d Polyline. We will stick to blocks for this discussion. You have to
actually pick the attribute sub-entity and not any sub-entity contained inside the block. You can then modify the DXF group
codes and update the attribute. This is fine for single selection and single manipulation of one attribute inside the block. I'll
give a quick demonstration of the NENTSEL function to satisfy those out there that are dying to try it. You know who you
are.
NENTSEL returns the exact same thing as ENTSEL if the entity selected is not a complex entity like a Block or 3d Polyline.
If you selected acomplex entity such as an attribute inside a block, NENTSEL would return something like this:
- 63 -
(<Entity name: 400a14a8> (5193.24 4935.03 0.0)( (20.0 0.0 0.0) (0.0 20.0 0.0) (0.0 0.0 20.0) (5237.78 4923.46 0.0)) (<Entity
name: 40222278>))
(<Entity name: 400a14a8> - The actual entity name of the entity selected.
(20.0 0.0 0.0) - We will ignore the Model to World Transformation Matrix
(0.0 20.0 0.0) - We will ignore the Model to World Transformation Matrix
(0.0 0.0 20.0) - We will ignore the Model to World Transformation Matrix
(5237.78 4923.46 0.0) - We will ignore the Model to World Transformation Matrix
(<Entity name: 40222278>) - The entity name of the block that contains the selected entity
To get the DXF Group codes of the selected attribute using NENTSEL :
(if (setq ent(nentsel "\n Select Attribute: ")) ;- Select the attribute
(progn
That's enough on NENTSEL. Let's get back to work finding attribute information. First thing we need to do is let the user
select a block :
Then let's get the entity name and dxf group codes of the block:
- 64 -
(setq enlist(entget en))
Let's take it apart, put it in order, and get rid of the codes that are not needed:
Note: If you want to see the other group codes please see the Insert / Block entity on this page.
Notice the red code. Code number 66. This is the important one for finding attributes. If this code has a value of zero, there
are no attributes in the block. If it has a value of 1, the block contains attributes. Simple! You find the attributes by stepping
through the block using ENTNEXT until you reach the SEQEND entity.
(if (setq ent(entsel "\n Select a Block: ")) ;- Let the user select a block
(progn
- 65 -
(setq blkType(cdr(assoc 0 enlist))) ;- Save the type of entity
(progn
(if(= (cdr(assoc 66 enlist)) 1) ;- See if the attribute flag equals one (if so, attributes follow)
(progn
(while (/= (cdr(assoc 0 enlist2)) "SEQEND") ;- Start the while loop and keep
;- looping until SEQEND is found.
Finally we get to see the Attribute DXF group codes which should be printed out on your command line or text screen. They
should look something like this:
- 66 -
(
(5 . "862") 5 - Handle
- 67 -
(210 0.0 0.0 1.0) 210 - Extrusion direction (ignore)
To get the value of the attribute using the program from above:
(cdr(assoc 1 enlist2))
(cdr(assoc 2 enlist2))
(entmod enlist2)
(entupd en2)
Phew....I think we got through that unscathed. If you have any questions <Send Email>
Circle
Typical DXF Group Codes for a Circle Entity:
To list a circle in AutoCAD you type LIST<enter> and select the circle. To do this in AutoLisp you would:
Would return:
- 68 -
(-1 . <Entity name: 37b0650>) -1 - AutoCad Entity Name (AutoCAD does this Automatically)
(cdr(assoc 10 enlist))
(cdr(assoc 8 enlist))
Ellipse
Typical DXF Group Codes for an ELLIPSE Entity:
To list an ellipse entity in AutoCAD you type LIST<enter> and select the ellipse. To do this in AutoLisp you would:
- 69 -
(setq en(car (entsel "\n Select an Ellipse :")))
(setq enlist(entget en))
Would return:
(-1 . <Entity name:1cb0670>) -1 - Entity Name (AutoCAD Automatically takes care of this)
(11 21.2132 -21.2132 0.0) 11 - end Point of Major axis (relative to center)
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Factor
- 70 -
To get the length of the major axis :
Image
To list an image entity in AutoCAD you type LIST<enter> and select the image. To do this in AutoLisp you would:
- 71 -
(-1 . <Entity name: 40073da8>) -1 Entity Name (AutoCAD Automatically takes care of this)
(11 0.0012987 0.0 0.0) 11 - U-vector of a single pixel (points along the visual bottom
of the image, starting at the insertion point) (in OCS)
(12 7.95199e-020 0.0012987 0.0) 12 - V-vector of a single pixel (points along the visual left
of the image, starting at the insertion point) (in OCS)
(14 769.5 558.5 0.0) 14 - Other corner for rectangular. See (1) above.
- 72 -
(282 . 50) 282 - Contract Value 0 to 100 (Default = 50)
(340 . <Entity name: 40073d98>) 340 - Ignore Hard reference to imagedef object
(360 . <Entity name: 40073da0>) 360 - Ignore Hard reference to imagedef_reactor object
(cdr(assoc 8 enlist))
Insert / Block
Typical DXF Group Codes for an INSERT Entity:
To list an insert entity in AutoCAD you type LIST<enter> and select the block. To do this in AutoLisp you would:
Would return:
- 73 -
Let's take it apart and put it in order:
(-1 . <Entity name:1c90970>) -1 - Entity Name (AutoCAD Automatically takes care of this)
0 = Default
1 = Attributes-follow flag
A series of attribute entities is expected to follow the insert,
terminated by a SEQEND entity.
Block flags:
0 = Normal
1 = This is an anonymous block generated by hatching,
associative dimensioning, other internal operations,
or an application
2 = This block has attribute definitions
4 = This block is an external reference (xref)
- 74 -
8 = This block is an xref overlay
16 = This block is externally dependent
32 = This is a resolved external reference,
or dependent of an external reference
(ignored on input)
64 = This definition is a referenced external reference
(ignored on input)
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Direction
(cdr(assoc 2 enlist))
(cdr(assoc 8 enlist))
(cdr(assoc 70 enlist))
(cdr(assoc 41 enlist))
- 75 -
Line
Typical DXF Group Codes for a LINE Entity:
To list a line in AutoCAD you type LIST<enter> and select the line. To do this in AutoLisp you would:
Would return:
(-1 . Entity name: 37b0648) -1 - AutoCad Entity Name (AutoCAD does this Automatically)
(210 0.0 0.0 1.0) Don't worry about this. This is default unless extruded.
To get the length of the line we need the distance from the start point to the end point :
- 76 -
(distance (cdr (assoc 10 enlist)) (cdr(assoc 11 enlist)) )
To get the angle of the line we need two points. The start point and the end point :
(cdr(assoc 8 enlist))
LWPolyLine
Typical DXF Group Codes for an LWPOLYLINE Entity:
To list an polyline entity in AutoCAD you type LIST<enter> and select the polyline. To do this in AutoLisp you would:
Would return:
(-1 . <Entity name:1cb0658>) -1 - Entity Name (AutoCAD Automatically takes care of this)
NOTE:
- 77 -
The next series of codes are Vertex points (group 10 codes) followed by group 40 codes.
These codes always appear in the list by order in which they were created.
10 - Vertex 2D point
40 - Starting Width of the polyline. This is optional. Default = 0.
Code 40 is not used if group code 43 is set.
41 - Ending Width of the polyline. This is optional. Default = 0.
Code 41 is not used if group code 43 is set.
42 - Bulge factor. Used to create arcs in the polyline.
43 - Constant Width. Optional. Default = 0.
Code 43 is not used if codes 40 and 41 are set.
FLAGS:
0 - Default
1 - Closed
128 - PLINEGEN (Generation of LineType)
- 78 -
(100 . "AcDbEntity") 100 - Don't worry about this
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Factor
Unfortunately, that is as far down as you can dig into the list. To get all of the group 10 codes,
you will have to manually search for them.
Here's an example:
( (1.0 1.0) (2.0 1.0) (2.0 2.0) (3.0 3.0) (1.0 4.0) )
(cdr(assoc 8 enlist))
- 79 -
(princ "\n It is not a LWPolyLine")
)
MLine
Typical DXF Group Codes for a MLine Entity:
To list a MLine in AutoCAD you type LIST<enter> and select the MLine. To do this in AutoLisp you would:
- 80 -
(71 . 3) 71 - Flags - 1=Unlocked 2=Closed
4=Suppress start caps 8=Suppress end caps.
Note: Each vertex will have a code 11, 12, 13, 74, 41, and 75. I'm going to include the first appearance of codes 12, 13,
74, and 75 and delete the rest for clarity. I'll explain later.
(12 1.0 0.0 0.0) 12 - Direction Vector for next line from this point
(12 0.0 1.0 0.0) 12 - Second Direction vector for next line
(12 -1.0 0.0 0.0) 12 - Third Direction vector for next line
(12 0.0475993 -0.998867 0.0) 12 - Fourth Direction vector for next line
- 81 -
(330 . <Entity name: 40073cf8>) 330 - Ignore
(340 . <Entity name: 40073cc0>) 340 - Ignore (needed to modify MLineStyle. See note below.
The group code 41 may contain zero or more items. The first group code 41 value is the distance from the segment vertex
along the miter vector to the point where the line element's path intersects the miter vector. The next group code 41 value is
the distance along the line element's path from the point defined by the first group 41 to the actual start of the line element.
The next is the distance from the start of the line element to the first break (or cut) in the line element. The successive group
code 41 values continue to list the start and stop points of the line element in this segment of the mline. Linetypes do not affect
group 41 lists.
The 2 group codes in mline entities and mlinestyle objects are redundant fields. These groups should not be modified under
any circumstances, although it is safe to read them and use their values. The correct fields to modify are as follows:
Mline - The 340 group in the same object, which indicates the proper MLINESTYLE object.
Mlinestyle -The 3 group value in the MLINESTYLE dictionary which precedes the 350 group that has the handle or entity
name of the current mlinestyle.
(setq en(car(entsel "\n Select a MLine: "))) ;;;--- Get the entity name
(setq myVertexList(list)) ;;;--- create an empty list to store the vertices in.
(setq v1(cdr(assoc 10 enlist))) ;;;--- Get the starting point of the mline
(setq myVertexList(append myVertexList (list v1))) ;;;--- Add the point to the list
- 82 -
( (11.4446 15.392 0.0) (11.4446 15.392 0.0)(30.3875 15.392 0.0)(30.3875 31.4532 0.0)
(10.6793 31.4532 0.0) )
(command "line" (car myVertexList)) ;;;--- Start the line command with the first vertex
(foreach a (cdr myVertexList) ;;;--- Cycle through the rest of the vertex list
MText
Typical DXF Group Codes for an MText Entity:
To list MText in AutoCAD you type LIST<enter> and select the MText. To do this in AutoLisp you would:
Would return:
((-1 . <Entity name: 40222a10>) (0 . "MTEXT") (330 . <Entity name: 40073cf8>) (5 . "942") (100 . "AcDbEntity") (67 . 0)
(410 . "Model") (8 . "0") (100 . "AcDbMText") (10 37.2758 63.7667 0.0) (40 . 0.2) (41 . 54.9151) (71
. 1) (72 . 5) (1 . "This is an Mtext string. ") (7 . "Standard") (210 0.0 0.0 1.0) (11 1.0 0.0 0.0) (42 . 4.2) (43 . 0.266667) (50 .
0.0) (73 . 1) (44 . 1.0))
(1 . "This is an Mtext string. ") 1 - Text Value (See Note 1 and 2 Below!!!)
- 83 -
(10 37.2758 63.7667 0.0) 10 - Insertion Point
NOTE 1 : Formatting!
MText can contain formatting characters. For instance, if I change the MText entity we used in the example above:
to use the Arial font, underlined, and bold, the MText value would look something like this:
- 84 -
(1 . "{\\fArial|b1|i0|c0|p34;\\LThis is an Mtext string. \\Ftxt.shx; }")
Note: Use curly braces ({ }) to apply a format change only to the text within the braces. You can nest braces up to eight levels
deep.
If the number of characters in an MText entity is less than 250, all characters will be in group code number 1. If you exceed
249 characters, the characters are broken up in 250 character chunks and placed under group code number 3. The remainder
of the characters will be in group code 1. For instance, if I change the MText entity we used in the example above:
into a string that is 600 characters long, the MText value would look something like this:
(1 .
"1234567899123456789012345678911234567892123456789312345678941234567895123456789
61234567897123456789812345678991234567890 ")
Where's the rest of the string? It is stored in the group 3 codes. There are two if them that are 250 characters each.
(3 .
"1234567890123456789112345678921234567893123456789412345678951234567896123456789
71234567898123456789912345678901234567891123456789212345678931234567894123456789
51234567896123456789712345678981234567899123456789012345678911234567892123456789
31234567894")
(3 .
"1234567895123456789612345678971234567898123456789912345678901234567890123456789
11234567892123456789312345678941234567895123456789612345678971234567898123456789
91234567890123456789112345678921234567893123456789412345678951234567896123456789
71234567898")
To put the string back together, you would have to use the STRCAT function on the group 3 codes first:
- 85 -
(setq myStr "")
(foreach a enlist
Then you would have to tack the string stored in group code 1 to the end. Like this:
(cdr(assoc 10 enlist))
(cdr(assoc 8 enlist))
(cdr(assoc 1 enlist)) ;if it less than 250 characters long (See Note 1 Above)
(cdr(assoc 50 enlist))
- 86 -
(if (= "MTEXT" (cdr(assoc 0 enlist)))
Point / Node
Typical DXF Group Codes for an Point or Node Entity:
To list a point entity in AutoCAD you type LIST<enter> and select the point. To do this in AutoLisp you would:
Would return:
(-1 . <Entity name: 4009ff78>) -1 - Entity Name (AutoCAD Automatically takes care of this)
(50 . 0.0) 50 - Angle of the X axis for the UCS in effect when the point
was drawn if PdMode does not = 0 (default = 0)
- 87 -
Let's play with it:
Polyline
Typical DXF Group Codes for a Polyline Entity:
To list a polyline entity in AutoCAD you type LIST<enter> and select the polyline. To do this in AutoLisp you would:
Would return:
- 88 -
(70 . 4) 70 - PolyLine flag
0 = Default (No flags) 1 = This is a closed polyline or mesh closed in
M direction. 2 = Curve fit Vertices have been added
4 = Spline fit Vertices have been added
8 = This is a 3D Polyline
16 = This is a 3D polygon Mesh
32 = This polygon Mesh is Closed in the N Direction
64 = This polygon is a polygon Mesh
128 = Generate Line Type Continuously around Vertices
Notice there are no vertices shown. You have to step through this entity looking at the sub-entities for the vertices until you
come across the SEQEND entity. Sound familar? It should. This is exactly like the Attribute Entity. Pretty much anyway.
Please read the Attribute section above for instructions on the ENTNEXT function used to step into an entity looking for sub-
entities and the SEQEND entity definition. When you get through, come back to here.
So, we now know how to step into an entity. Let's do that for this polyline and save a list of all of the vertices.
(defun C:PTEST()
- 89 -
;;;--- Get the sub-entities name
(setq en2(entnext en))
)
)
(princ)
)
If you run this program and then type this in the command line:
Command: ptList!<enter>
It will return a list of all of the vertices and will look something like this:
((48.4773 56.3423 0.0) (47.9891 57.1226 0.0) (47.7463 57.7535 0.0) (47.7072
58.2456 0.0) (47.8304 58.6095 0.0) (48.0741 58.8559 0.0) (48.3968 58.9954 0.0)
(48.7568 59.0385 0.0) (49.1125 58.9959 0.0) (49.4264 58.8788 0.0) (49.6767
58.7011 0.0) (49.8457 58.4773 0.0) (49.9157 58.2219 0.0) (49.869 57.9495 0.0)
(49.688 57.6745 0.0) (49.355 57.4114 0.0) (48.8522 57.1748 0.0))
Let's get the start point of the polyline from the point list:
Let's get the end point of the polyline from the point list:
- 90 -
Let's get the Line Type of the polyline:
(if(cdr(assoc 6 enlist))
(setq plineLtyp(cdr(assoc 6 enlist)))
(setq plineLtyp "BYLAYER")
)
(if(cdr(assoc 62 enlist))
(setq plineClr(cdr(assoc 62 enlist)))
(setq plineClr "BYLAYER")
)
Solid
Typical DXF Group Codes for an SOLID Entity:
To list a solid entity in AutoCAD you type LIST<enter> and select the solid. To do this in AutoLisp you would:
Would return:
(-1 . <Entity name:2b80648>) -1 - Entity Name (AutoCAD Automatically takes care of this)
- 91 -
(39 . 0.0) 39 - Thickness. (optional) Default = 0.0
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Factor
(cdr(assoc 11 enlist))
(cdr(assoc 8 enlist))
Text
Typical DXF Group Codes for a Text Entity:
To list a text entity in AutoCAD you type LIST<enter> and select the text. To do this in AutoLisp you would:
Would return:
- 92 -
(71 . 0) (72 . 0) (11 0.0 0.0 0.0) (210 0.0 0.0 1.0) (100 . "AcDbText")
(73 . 0))
(-1 . <Entity name: 37b0658>) -1 - Entity Name (AutoCAD Automatically takes care of this)
(10 23.4375 9.1875 0.0) 10 - Start Point for the text entity
(72 . 0) Don't worry about this. 72 and 73 work together for alignment.
(73 . 0) Don't worry about this. 72 and 72 work together for alignment.
(210 0.0 0.0 1.0) Don't worry about this Extrusion Factor.
- 93 -
Remember, CDR returns everything after the first item in a list.
(cdr(assoc 10 enlist))
(cdr(assoc 8 enlist))
(cdr(assoc 1 enlist))
(cdr(assoc 50 enlist))
Trace
Typical DXF Group Codes for a TRACE Entity:
To list a trace entity in AutoCAD you type LIST<enter> and select the trace. To do this in AutoLisp you would:
Would return:
(-1 . <Entity name:1cb06f8>) -1 - Entity Name (AutoCAD Automatically takes care of this)
- 94 -
(8 . "STR") 8 - Layer Name
(210 0.0 0.0 1.0) 210 - Don't worry about this Extrusion Factor
(cdr(assoc 11 enlist))
(cdr(assoc 8 enlist))
Note: Notice the resemblance to the SOLID entity. The only difference being,
the TRACE requires FOUR corners to define itself.
XLine
- 95 -
Typical DXF Group Codes for an XLine Entity:
To list an XLine entity in AutoCAD you type LIST<enter> and select the XLine. To do this in AutoLisp you would:
Would return:
(-1 . <Entity name: 40222960>) -1 - Entity Name (AutoCAD Automatically takes care of this)
Note: There's not much you can do with an XLine so, let's let it go at that. Get on away from here Miss Daisy!
- 96 -
The AutoLisp Tutorial - Extreme
Well you made it. You've read the Beginners, Intermediate, and Expert's tutorials and now you want to apply your
knowledge in real world situations. I believe the best way to approach this is to read through commented code and follow
what is going on. It would be even better if you had the programmers thoughts before starting the program. So, we will do
that. But what code? This I will leave up to you. If you have read all of the tutorials and still can't get the code to do what
you want, click <here> to suggest a program for the Extreme section. I'll start it with a couple of problems I've encountered in
the last year or so. Placing vertices along a polyline bulge and figuring the angle of an arc. I know, seems simple. But what
happens when your start angle is around 350 degrees and your end angle is around 30 degrees. You can't subtract the end
angle from the start angle can you?
PolyLine Bulge
Have you ever tried to recreate a polyline by drawing lines from vertex to vertex only to find out it doesn't work if you have a
polyline bulge? (ARC) Aggravating isn't it. Well, I've found a method to insert vertex points along the polyline bulge area to
recreate a bulge effect and get you closer to what you need. I'm looking for this code....
Arc Angle
Have you ever tried to figure the angle of an arc only to be fustrated by the fact the angle cannot be simply subtracted? It's
true because an arc's angle is always measured counter clock-wise so you could have a starting angle of 350 degrees and an
ending angle of 30 degrees. Can't subtract the end angle from the start angle can you? When it crosses zero, there are
problems. (Angle could also be set to clock-wise instead of counter clock-wise)
The first method I used was to begin at the starting angle and increment the angle by one until I reached the end angle or I
crossed the zero angle. This works but it is a little goofy.
The second method I've seen (sent to me by a visitor) was to reset the UCS to the center point with an angle that matched the
starting angle. Then simply get the ending angle. Not bad. Not bad at all.
The shortest method I've found was to use the built in geometry calculator:
- 97 -
The calculator function we will use is called ANG. The syntax is ANG(apex, endPt1, endPt2). In the illustration above the
apex is P1, the first point is P2, and the third point is P3. You can also do this on the command line. Type CAL and press
enter. Then type ang(cur,cur,cur) and press enter. Now pick the three points.
- 98 -
Contents
• Getting Started
• Rows and Columns
• Controls
• Image
• Action
• Set_Tile and Mode_Tile
• List and how to handle them.
• Saving data from the dialog box
• Part 1 - Buttons
• Part 2 - Edit_Box
• Part 3 - List_Box
• Part 4 - PopUp_List
• Part 5 - Radio_Buttons
• Part 6 - Text and Toggle
• Part 7 - Putting it all together
Getting Started
I'm not going to explain everything there is to know about Dialog Control Language. There are plenty of books on the
subject. I will attempt to give you a good understanding of how DCL interacts with AutoLisp. You should be able to write
your first DCL driven Autolisp program in no time. Let's get to it.
Here is the basic order for things to happen inside the AutoLisp file:
- 99 -
(defun myFunction1() ; Define the functions you need like saveVars
(defun myFunction2()
(setq list1(list "a" "b" "c" "d")) ; Build the list if any are required
;Two action tiles need to have the done_dialog call. One of these needs to save the settings before
; the done_dialog call.
- 100 -
unload_dialog ; Unload the dialog box
(if (= d 1)
(if (= d 2)
I've found the hardest thing to get a grip on is laying out the dialog box. Please take a look at the rows and columns section
if you need help with laying out a dialog box. For now let's look at the basics. I'm going to throw a lot at you, but don't panic.
I'll cover them a little closer later on. We will cover these items first: button, row, column, boxed_row, and boxed_column.
Okay and Cancel Buttons - The DCL code for a these buttons look like this:
Rows and Columns designate areas to lay your controls on. [Controls are list boxes, edit boxes, buttons, etc. ].
: column {
- 101 -
}
: row {
Simple right?
: column {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false; Notice the buttons are stacked on top of each other.
is_cancel = true;
}
: row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
Notice the buttons are side by side.
is_default = false;
is_cancel = true;
}
Let's turn the above into Boxed Rows and Boxed Columns. Here is the code for a boxed_column with two buttons:
- 102 -
: boxed_column {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
Notice the box around the buttons.
is_cancel = true;
}
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false; Notice the box around the buttons.
is_cancel = true;
}
Now we know the difference between a row and a boxed_row. We also know that controls inside a column get stacked on
top of each other [ vertical ] and controls inside a row are next to each other [ horizontal ].
Important: You should never ever attempt to build a dialog box without a cancel button. Trust me. Or you can do what I
did and find out for yourself.
Overview
Let's take a look at the entire code for the dialog box above, including the LSP file:
- 103 -
DCL_TEST : dialog {
: boxed_row {
: button {
key = "accept"; (defun C:DCL_TEST()
label = " Okay "; (setq dcl_id (load_dialog "DCL_TEST.dcl"))
(if (not (new_dialog "DCL_TEST" dcl_id) ) (exit))
is_default = true; (action_tile "cancel" "(setq ddiag 1)(done_dialog)")
} (action_tile "accept" "(setq ddiag 2)(done_dialog)")
(start_dialog)
(unload_dialog dcl_id)
(if (= ddiag 1)
: button { (princ "\n \n ...DCL_TEST Cancelled. \n ")
key = "cancel"; )
(if (= ddiag 2)
label = " Cancel "; (alert "You pressed the OKAY button!")
is_default = false; )
)
is_cancel = true;
}
AutoLISP
(defun C:DCL_TEST()
The second thing was to load the DCL file from disk. Insert a path if necessary, but I always liked to use the autocad search
path to find the file. Notice the dcl_id variable used. We will need this later on. This identifies the file opened.
Since a DCL file can contain multiple dialog box definitions, [ I'll show you an example later on. ] we need to specify which
dialog box we want to load. This is taken care of in the next statement. Notice the name matches the definition inside the
DCL file.
Next, we define our action keys. These tell the program what to do whenever a user presses a button,
selects an item in a list, etc. Notice the two action keys. "cancel" and "accept" . These match the
keys in the DCL file. I made these names up. You can name them whatever you want as long as it is not
a name used by AutoCAD and it only appears once in each DCL definition. You can't have two controls
- 104 -
with the same key, the program would get confused. So would I!!! Whenever the user presses the
cancel key, the program sets variable ddiag to 1 and closes the dialog box. Upon pressing the Okay
button, [ key = accept ], the program sets variable ddiag to 2 and closes the dialog box. That's it.
Nothing else to add except, you could have just about anything in here, including instructions to
execute a function [ I'll demonstrate that later. ]
Finally, the big step. Everything is defined, let's display the dialog box.
The program halts here until the dialog box is issued a "done_dialog" call. In the mean time the
user is interacting with the action keys. When the user presses a button the program kills the dialog
box with the unload_dialog call. Notice the dcl_id passed as a parameter. You could have more than
one dialog file open at one time. So, the unload_dialog function needs to know which one to unload.
Finally the dialog box is gone. What next? Find out which button the user pressed? Exactly!
Remember setting the ddiag variable to either 1 or 2. We will use that now to determine which button
was pressed. There is a better way to do this, I'll show you later.
- 105 -
The DCL File:
EXAMPLE : dialog {
label = "EXAMPLE.lsp"; \\ Puts a label on the dialog box
initial_focus = "textval"; \\ Sets the initial focus
: column {
: row {
: boxed_column {
: edit_box { \\ The edit_box control - Something new!
key = "textval";
label = "Text Value to Find";
edit_width = 30;
value = "";
}
}
}
: row {
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
}
}
(defun saveVars()
;;;--- Save the input from the dialog box
(setq textVal (get_tile "textval"))
)
(defun C:EXAMPLE()
- 106 -
;;;--- Display the dialog box
(start_dialog)
Screen Shot:
If you could not follow some of the autolisp functions, you can ignore them for now or read the Autolisp Tutorial.
This is the model. Very little will change from this setup. You may add some controls to the DCL file which in turn will add
Action calls and influence the number of lines inside the saveVars routine. The only other thing that will change is inside the
OKAY button function. [ When ddiag = 2 ]. You will have to tell the program what to do when all of the information is
gathered. We will cover these later.
Let's get a feel for laying out a dialog box using nested rows and columns. I'll do this by showing you the DCL code next to
a picture of the dialog box. I can't think of any easier way to do it. Ignore all of the Labels and TEXT controls. I will use
these to visually show the location of things.
- 107 -
Example 1:
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_column {
label = "column 1";
: boxed_column {
label = "column2";
}
: boxed_column {
label = "column 3"; Column
} Column
} Column<\p>
ok_cancel;
}
}
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_row {
label = "row 1";
: boxed_column {
label = "column2";
}
: boxed_column {
label = "column 3"; Notice I changed the first boxed column into a boxed row.
}
} Row
ok_cancel; Column
} Column
}
- 108 -
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_column {
label = "column 1";
: boxed_column {
label = "column2"; : text
{
key = "t1";
value = "Text1";
}
: text {
key = "t2";
value = "Text2";
}
}
: boxed_column {
label = "column 3"; :
text {
key = "t3";
value = "Text3";
} Column
: text { Column
key = "t4"; Column<\p>
value = "Text4";
}
}
}
ok_cancel;
}
}
- 109 -
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_row {
label = "row 1";
: boxed_column {
label = "column2"; : text
{
key = "t1";
value = "Text1";
}
: text {
key = "t2";
value = "Text2";
}
}
: boxed_column {
label = "column 3"; :
text {
key = "t3"; Row
value = "Text3"; Column
} Column
: text {
key = "t4";
value = "Text4";
}
}
}
ok_cancel;
}
}
- 110 -
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_row {
label = "row 1";
: boxed_row {
label = "row 2";
: text {
key = "t1";
value = "Text1";
}
: text {
key = "t2";
value = "Text2";
}
}
: boxed_row {
label = "row 3"; :
text {
Row
key = "t3";
Row
value = "Text3";
} Row
: text {
key = "t4";
value = "Text4";
}
}
}
ok_cancel;
}
}
- 111 -
EXAMPLE : dialog {
label = "EXAMPLE.lsp";
: column {
: boxed_column {
label = "row 1";
: boxed_row {
label = "row 2";
: text {
key = "t1";
value = "Text1";
}
: text {
key = "t2";
value = "Text2";
}
}
: boxed_row {
label = "row 3"; :
text {
key = "t3";
value = "Text3"; Column
} Row
: text { Row
key = "t4";
value = "Text4";
}
}
}
ok_cancel;
}
}
I hope this puts an end to the rows and columns issue. If not, you know where to find me.
CONTROLS
Let's take a look at the different controls you can put on the dialog box. You've seen a few of them in the overview portion but
let's go over the majority of them now. I will only show the most used properties for each control.
- 112 -
LAYOUT CONTROLS:
: column { Column
label = "My Column";
} You can omit the label.
: row { Row
label = "My Row";
} You can omit the label.
: spacer { A normal spacer to help align other controls. You can omit the width and
width = 10; height properties.
height = 15;
}
: spacer_0; A spacer that usually has no width. It simply means, if you have to stretch
something in this row to make things fit, stretch this spacer instead of the other
items..
BUTTON
- 113 -
BOXED RADIO COLUMN, RADIO COLUMN, & RADIO BUTTON
- 114 -
EDIT BOX
: edit_box {
key = "myval"; // Action key
label = "Value:"; // Label for the edit box
edit_width = 10; // Character width
value = ""; // Initial value
}
LIST BOX
: list_box {
label ="Choose Items"; // Label for the list box
key = "mylist"; // Action key
height = 15; // Height of list box
width = 25; // Width of list box
multiple_select = true; // Multiple Select = TRUE
fixed_width_font = true; // Use fixed with font = TRUE
value = "0"; // Initial selection = 1st item
} Fixed Width Font = TRUE
POPUP LIST
: popup_list {
key = "mylist"; // Action key
label = "Select Item"; // Label
fixed_width_font = false; // Use fixed with font = false
width = 15; // Width of list box
value = 0; // Initial selection = 1st item
}
- 115 -
TEXT
: text {
key = "mytext"; // Action key
value = "This is text!"; // Value
}
Okay. That's it for controls. There are others but, these are the most used controls. Let's move on
Image
Image
An Image is used to display a vector graphic picture inside a rectangle. To create the image you can use three different
methods.
For this tutorial we will concentrate on the Slide_Image function. Everyone knows how to create a slide right? The MSLIDE
command. Okay. We will assume our image is ready.
Let's look at the DCL Image definition and the AutoLisp functions required to display the image.
DCL - You must supply either the width and height or one of these plus an aspect_ratio.
: image {
key = "sld";
height = 30;
width = 30;
color = 0;
is_enabled = false;
is_tab_stop = false;
}
AutoLisp
;;;--- Next we send the slide name and the key to the update function
(upDateImage mySlideName myKey)
- 116 -
; NOTE: Notice mySlideName becomes sldName and myKey becomes key when passed
; as parameters to the upDateImage function.
This function can be used over and over to display any slide image in an image control. Use the upDateImage function after
the new_dialog call and before the action_tile statements. You can also use this function while the dialog box is displayed in
one of your action calls. We will use this later on in the tutorial so you can see it in action.
Action
In order for the dialog box to respond to user interaction, you must set up an action event. A trigger, to fire when the user
selects an item from a list or presses a button. This works off of the KEY defined for each control inside the DCL file. If the
key for a button is named "mybutton" then you must have an action named "mybutton". Otherwise the user will press the
button and nothing will happen. Let's take a look at the action_tile.
"Key" - The name of the key you defined with the control inside the DCL file.
- 117 -
"Action"- The action you want taken when the action event is fired. You can set a variable or run a function.
Examples:
Let's take for example, you have a button named "test" and you want to set the variable named myTEST to 1 if the user
presses the button:
Notice the key is in quotes and the setq function is in quotes. This is standard procedure for the action_tile statement. Next
let's say you have the same thing except you want to run a function named "saveVars" when the user presses the test button.
What if you wanted to do both, run the function and set a variable?
One more thing....What if the button is set to be the cancel or accept button for the dialog box? No problem...
That is about all there is to an action_tile. Remember, anything with a key can have an action call.
"Key" - The name of the key you defined with the control inside the DCL file.
• Edit_box
o (set_tile "mybox" "Jeff") Displays Jeff in the edit box.
o (set_tile "mybox" "4'-3 1/2") Displays 4'-3 1/2 in the edit box.
- 118 -
• List_box
o (set_tile "mylist" "0") Selects the first item in the list.
o (set_tile "mylist" "5") Selects the sixth item in the list.
o (set_tile "mylist" "") No Items are selected.
• PopUp_List
o (set_tile "mylist" "0") Selects the first item in the list.
o (set_tile "mylist" "5") Selects the sixth item in the list.
o (set_tile "mylist" "") No Items are selected.
• Toggle
o (set_tile "mytog" "0") Removes the check from the box.
o (set_tile "mytog" "1") Checks the box.
• Radio_Button
o (set_tile "myradio1" "1") Moves the selection to the first radio button.
o (set_tile "myradio2" "1") Moves the selection to the 2nd radio button.
Use Set_Tile after the new_dialog and before the action_tiles. Set_Tile can also be used as a function while the dialog box is
displayed.
"Key" - The name of the key you defined with the control inside the DCL file.
Use Mode_Tile after the new_dialog call and before the action_tiles. Mode_Tile can also be used as a function while the
dialog box is displayed.
That is all there is to set_tile and mode_tile. We will used them later on in the tutorial so you can see them in action.
List_box and popup_list handle a list of items in basically the same manner. You have to load your list into the list box and
at some point you have to decide which item or items were selected. In the "Saving data from a dialog box" I cover how to
find the selected items from a list_box and a popup_list. How do we get the list inside the list_box? That is what we need to
cover in this section. So let's get started.
- 119 -
If you looked at the previous sections you know the AutoLisp and DCL basic model. Let's get a copy of that in this section
so we can look at it. I will replace the edit_box with a list_box control and add the code to handle the list. The list handling
code is shown in the "Saving data from a dialog box" section of this tutorial.
I will show the revised and new code in red below. All of the black code is unchanged from the Basic Model and the
instructions from the "list_box multiple selection" area of the "Saving data from a Dialog box" page.
EXAMPLE : dialog {
label = "EXAMPLE.lsp"; \\ Puts a label on the dialog box
: column {
: row {
: boxed_column {
: list_box { \\ I replaced the edit_box with a list_box.
label ="Choose Items";
key = "mylist";
height = 15;
width = 25;
multiple_select = true; \\ Multiple selection is on
fixed_width_font = true; \\ Fixed Width Font is on
value = "0";
}
}
}
: row {
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
}
}
;;;--- EXAMPLE.lsp
;;;--- I replaced the saveVars routine with the one from the "Save data from a list" section of this tutorial.
;;; I also changed the things marked in red.
- 120 -
(defun saveVars(/ readlist count item)
;;;--- Notice the "mylist" is the action key associated with the DCL file and
;;; the myList is the variable for the list built below.
;;;--- cycle through the list getting all of the selected items
(while (setq item (read readlist))
(setq retlist(append retList (list (nth item myList))))
(while
(and
(/= " " (substr readlist count 1))
(/= "" (substr readlist count 1))
)
(setq count (1+ count))
)
(setq readlist (substr readlist count))
)
)
(defun C:EXAMPLE()
;;;--- Notice the "mylist" is the action key associated with the DCL file and
;;; the myList is the variable for the list built above.
- 121 -
;;;--- If the cancel button was pressed - display message
(if (= ddiag 1)
(princ "\n \n ...EXAMPLE Cancelled. \n ")
)
So, in order to display a list in a dialog box, you must first build the list. Then use the start_list, add_list, and end_list
functions just after the new_dialog call and before the action_tiles. This is the same for both a list_box and a popup_list. That
wasn't so bad. Was it?
In this section we are going to take a look at the SaveVars routine and disect it for each type of control. The types of controls
that we sometimes need to save are :
The SaveVars routine is executed just before the dialog box is issued a done_dialog call. You cannot get the data from the
dialog box once it has been shut down. If you remember our action calls in the AutoLisp Program, they looked something like
this:
Notice the (saveVars) is just before the (done_dialog). This is the proper order. The settings from
the dialog box will be saved just before it is shut down.
(defun saveVars()
;stuff here
That's the shell. We need to work on the "stuffing". Let's get to it.
Stuffing
There are three parts to a control with an action call back statement.
- 122 -
1. The DCL definition
In each of the following controls I will show you all three parts so we know how to put it all
together later. Although, all of the parts are important, try to focus on the SaveVars routine.
Edit_Box
: edit_box {
key = "edbox";
label = "Bolt Circle Diameter:";
1 edit_width = 8;
value = "10";
}
No Action call required. We will get the data the user typed when we use the SaveVars routine. But, if your deadset
about doing something when the user types in the edit box, here you go:
2
(action_tile "edbox" "(doThisFunction)")
) )
NOTE: Notice I used the key name as the variable to store the data in. You will find this method makes it easier to
keep things straight. DCL is case sensitive so, I never capitalize key names, only autolisp variable names if they are
made up of more than one word. Like: thisIsMyVariable.
1 : list_box {
key = "mylist";
label = "Available Choices";
multiple_select = "FALSE"; // Sets single selection
width = 40;
- 123 -
height = 20;
fixed_width_font = true; // Equally spaced characters
value = ""; // Start with no item selected
}
No Action call required. We will get the selected item when we use the SaveVars routine. But, sometimes you need
to change things in the dialog box if the user makes a selection, so:
2
(action_tile "edbox" "(doThisFunction)")
(defun saveVars()
;;;--- If the index of the selected item is not "" then something was selected
(if(/= sStr "")
(progn
)
)
Notes:
1. This should only be used when single selection is required. This will not work on multiple selection.
: list_box {
key = "mylist";
label = "Available Choices";
multiple_select = "TRUE"; // Sets multiple selection
1 width = 40;
height = 20;
fixed_width_font = false; // Use default font [ no alignment ]
value = "4"; // Start with item 5 selected.
}
2 No Action call required. We will get the selected items when we use the SaveVars routine. But, sometimes you need
to change things in the dialog box if the user makes a selection, so:
- 124 -
(action_tile "mylist" "(doThisFunction)")
(defun saveVars(/ readlist count item)
;;;--- cycle through the list getting all of the selected items
(while (setq item (read readlist))
3 (setq retlist(append retList (list (nth item fileNames))))
(while
(and
(/= " " (substr readlist count 1))
(/= "" (substr readlist count 1))
)
(setq count (1+ count))
)
(setq readlist (substr readlist count))
)
)
Note: This method can be used for a single or multiple selection list_box.
PopUp_List
: popup_list {
key = "mylist"; // action key
fixed_width_font = false; // fixed width font off
1
width = 20; // width of popup list
height = 20; // height of popup list
}
No Action call required. We will get the selected item when we use the SaveVars routine. But, sometimes you need
to change things in the dialog box if the user makes a selection, so:
2
(action_tile "mylist" "(doThisFunction)")
(defun saveVars()
- 125 -
: radio_button { // First radio button
label = "Choice 1";
key = "choice1";
}
etc.
(defun saveVars()
)
3
OR
(defun saveVars()
- 126 -
Radio_Row And Radio_Buttons
etc.
3 (defun saveVars()
OR
(defun saveVars()
- 127 -
(setq choice2(atoi(get_tile "choice2"))) // 0 = not chosen 1 = chosen
(setq choice3(atoi(get_tile "choice3"))) // 0 = not chosen 1 = chosen
(setq choice4(atoi(get_tile "choice4"))) // 0 = not chosen 1 = chosen
Toggle
: toggle {
key = "tog1"; // Action key
1
label = "Name"; // Label
}
No Action call required. We will get the value of the toggle when we use the SaveVars routine. But, sometimes you
need to change things in the dialog box if the user makes a selection, so:
2
(action_tile "tog1" "(doThisFunction)")
(defun saveVars()
Ok_Cancel
Part 1 - Buttons
Let's build a working DCL file showing us exactly how to handle buttons.
- 128 -
We will build a DCL file containing three buttons plus a Close [ Cancel ] button. Each of the three buttons will display a
message when pressed.
Layout thoughts: I will place the buttons in a column, (stacked on top of each other). Then I'll put the Close button at the
bottom on a row of it's own. So...I'll need something like this:
: column {
: boxed_column {
: button {
// Put code for button 1 here
}
: button { ]
// Put code for button 2 here
}
: button { ]
// Put code for button 3 here
}
}
: boxed_row {
: button {
// Put code for the Close button here
}
}
}
Let's copy in the code for the header and all of the controls above. I'll show them in red. Notice the key names and labels had
to be changed.
SAMPLE1 : dialog {
label = "Sample Dialog Box Routine - Part 1";
: column {
: boxed_column {
: button {
key = "but1";
label = "Button 1";
is_default = false;
}
: button {
key = "but2";
label = "Button 2";
is_default = false;
}
: button {
key = "but3";
label = "Button 3";
is_default = false;
}
}
: boxed_row {
: button {
key = "cancel";
label = "Close";
- 129 -
is_default = true;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE1.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE1()
I removed several lines from the model. I took out the part that checked to see if we hit the Cancel or Okay buttons. We
don't need either in this program. I also removed the action tile for the okay button and removed "(setq ddiag 1)" from the
cancel button.
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE1.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
- 130 -
C:Sample1
Command:
Now type Sample1 and press enter. If everything went according to plan you should see this on your screen:
The buttons do not work yet. Except for the close button. It should work fine. We need to add the function to print three
different messages when the user presses each button. Let's send a parameter to the function to decide which message to
display. If the parameter equals 1 we will print the first message. If 2 then print the second message. If 3 print the third
message. Something like this:
(defun doButton(a)
(cond
((= a 1)(alert "Button 1 was pressed!"))
((= a 2)(alert "Button 2 was pressed!"))
((= a 3)(alert "Button 3 was pressed!"))
)
)
Now we need to add the action calls for each of the buttons:
This will send a parameter of 1,2, or 3 to the doButton function depending on which button is pressed.
Let's add the doButton function and the action calls to the autolisp program. It should look like this:
(defun doButton(a)
(cond
((= a 1)(alert "Button 1 was pressed!"))
((= a 2)(alert "Button 2 was pressed!"))
((= a 3)(alert "Button 3 was pressed!"))
)
)
(defun C:SAMPLE1()
- 131 -
;;;--- Load the dialog definition if it is not already loaded
(if (not (new_dialog "SAMPLE1" dcl_id))
(progn
(alert "The SAMPLE1.DCL file could not be loaded!")
(exit)
)
)
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE1() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
Part 2 - Edit_Box
Let's build a working DCL file showing us exactly how to handle edit boxes.
We will build a DCL file containing two edit boxes plus a Okay and Cancel button. The information in the two edit boxes
will be displayed after selecting the Okay button.
- 132 -
Layout thoughts: I will place the edit boxes in a column, (stacked on top of each other). Then I'll put the Okay and Cancel
buttons at the bottom in a row. So...I'll need something like this:
: column {
: boxed_column {
: edit_box {
// Put code for edit_box 1 here
}
: edit_box {
// Put code for edit_box 3 here
}
}
: boxed_row {
: button {
// Put code for the Okay button here
}
: button {
// Put code for the Cancel button here
}
}
}
Let's copy in the code for the header and all of the controls above. I'll show them in red. Notice the key names and labels had
to be changed.
SAMPLE2 : dialog {
label = "Sample Dialog Box Routine - Part 2";
: column {
: boxed_column {
: edit_box {
key = "username";
label = "Enter your Name:";
edit_width = 15;
value = "";
initial_focus = true;
}
: edit_box {
key = "userage";
label = "Enter your Age:";
edit_width = 15;
value = "";
}
}
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
- 133 -
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE2.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE2()
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE2.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
- 134 -
Command: (load "sample2") and press enter
C:Sample2
Command:
Now type Sample2 and press enter. If everything went according to plan you should see this on your screen:
Looking good so far. We need to add the SaveVars function to save the strings in the edit boxes when the Okay button is
pressed. Look at the blue text in the Sample2.lsp program above.
The edit box for the name needs to be a string. Dialog boxes return strings, so we do not need to modify it. All we have to
do is use the get_tile function.
The edit box for Age needs to be an integer. We will have to modify the get_tile results by using the ATOI function. This
function converts a string to an integer.
If we needed to convert to an Real number we would use DISTOF function instead of the ATOI function.
(defun saveVars()
(setq userName(get_tile "username"))
(setq userAge( atoi (get_tile "userage")))
)
(defun saveVars()
(setq userName(get_tile "username"))
(setq userAge(atoi(get_tile "userage")))
)
- 135 -
(defun C:SAMPLE2()
Last item. We need to replace the line in the program: (princ "\n The user pressed Okay!") with something to
modify and display the userName and userAge data. Let's do something simple. We will find out how many days old this
person is.
;;;--- Multiply the users age x 365 to get the number of days.
(setq userAge(* userAge 365))
Add the above to the file, save it and test it out. Everything working okay?
- 136 -
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE2() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
Part 3 - List_Box
Let's build a working DCL file showing us exactly how to handle list boxes.
We will build a DCL file containing two list boxes plus an Okay and Cancel button. We will set the first list box to single
selection and the second to multiple selection. The selected items will be displayed on the screen after the user presses the
Okay button.
Layout thoughts: I will place the list boxes in a row, (side by side). Then I'll put the Okay and Cancel buttons in a row at
the bottom of the dialog box. So...I'll need something like this:
: column {
: boxed_row {
: list_box {
// Put code for list_box 1 here
}
: list_box {
// Put code for list_box 2 here
}
}
: boxed_row {
: button {
// Put code for the Okay button here
}
: button {
// Put code for the Cancel button here
}
}
}
Let's copy in the code for the header and all of the controls above from the "Controls" section of this tutorial. I'll show them in
red. Notice the key names and labels had to be changed.
- 137 -
SAMPLE3 : dialog {
label = "Sample Dialog Box Routine - Part 3";
: column {
: boxed_row {
: list_box {
label ="Choose Item";
key = "mylist1";
height = 15;
width = 25;
multiple_select = false;
fixed_width_font = true;
value = "";
}
: list_box {
label ="Choose Items";
key = "mylist2";
height = 15;
width = 25;
multiple_select = true;
fixed_width_font = true;
value = "";
}
}
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE3.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE3()
- 138 -
(exit)
)
)
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE3.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
C:Sample3
Command:
Now type Sample3 and press enter. If everything went according to plan you should see this on your screen:
- 139 -
Looks good but, there is nothing to choose. Let's add some data to the list boxes. We will need two list. We will call the list
for the first list box myList1 and the second myList2.
Alrighty then, we have our list built. All we have to do is put them in the dialog box. We will use the start_list, add_list, and
end_list functions. Start_list tells DCL which list_box we are going to edit. The identification is made by using the list_box
KEY. The first list has a key of "mylist1" and the second has a key of "mylist2". So the start_list function would look like
this:
(start_list "mylist1" 3) ; The 3 means we want to delete the old contents and start new.
Next we use the add_list function to tell DCL which list to put in the list_box. We use the mapcar function to apply add_list
to each member in the list. Our list for the first list_box is named myList1. So...
Finally we use the end_list function to tell DCL to display the new contents because we are through editing the list.
(end_list)
(start_list "mylist1" 3)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 3)
(mapcar 'add_list myList2)
(end_list)
- 140 -
Let's add all of this to the AutoLisp program and see what it looks like.
(defun C:SAMPLE3()
(start_list "mylist1" 3)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 3)
(mapcar 'add_list myList2)
(end_list)
Notice the location of the red lines. We create the list before loading the dialog box. We add the list to the dialog box after it
is loaded with new_dialog and before the action_tile statements. This is the order you should use.
Looking good so far. We need to add the SaveVars function to save the selected items from the list boxes when the Okay
button is pressed. Look at the blue text in the Sample3.lsp program above.
- 141 -
Let's steal the saveVars routine from the list_box control on the "List and how to handle them" page of this tutorial and
modify it. I'll show the modifications in red.
;;;--- cycle through the list getting all of the selected items
(while (setq item (read readlist))
(setq retlist(append retList (list (nth item myList1))))
(while
(and
(/= " " (substr readlist count 1))
(/= "" (substr readlist count 1))
)
(setq count (1+ count))
)
(setq readlist (substr readlist count))
)
)
Wow! That was easy. Wait a minute, we have two list boxes. We will have to create a function out of this or simply copy
this and do it twice. For now, let's just do it twice.
;;;--- cycle through the list getting all of the selected items
(while (setq item (read readlist))
(setq retlist(append retList (list (nth item myList1))))
(while
(and
(/= " " (substr readlist count 1))
(/= "" (substr readlist count 1))
)
(setq count (1+ count))
)
(setq readlist (substr readlist count))
)
- 142 -
;;;--- Save the list setting
(setq readlist(get_tile "mylist2"))
;;;--- cycle through the list getting all of the selected items
(while (setq item (read readlist))
(setq retlist2(append retList2 (list (nth item myList2))))
(while
(and
(/= " " (substr readlist count 1))
(/= "" (substr readlist count 1))
)
(setq count (1+ count))
)
(setq readlist (substr readlist count))
)
)
(defun C:SAMPLE3()
(start_list "mylist1" 3)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 3)
(mapcar 'add_list myList2)
(end_list)
- 143 -
(princ "\n The user pressed Okay!")
)
)
Last item. We need to replace the line in the program: (princ "\n The user pressed Okay!") with something to
modify and display the selected items. Let's do something simple. We will tell the user what was selected out of each list..
;;;--- Inform the user of his selection from the first list
(princ (strcat "\n You chose " (car retList) " from the first list box."))
;;;--- Inform the user of his selections from the second list
(princ "\n Your choice(s) from the second list box :")
(foreach a retList2
(princ "\n ")
(princ a)
)
)
)
Add the above to the file, save it and test it out. Everything working okay?
- 144 -
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE3() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
Part 4 - PopUp_List
Let's build a working DCL file showing us exactly how to handle popup list.
We will build a DCL file containing two pop_up list plus an Okay and Cancel button. The selected items will be displayed
on the screen after the user presses the Okay button.
Layout thoughts: I will place the popup_list in a row, (side by side). Then I'll put the Okay and Cancel buttons in a row at
the bottom of the dialog box. So...I'll need something like this:
: column {
: boxed_row {
: popup_list {
// Put code for popup_list 1 here
}
: popup_list {
// Put code for popup_list 2 here
}
}
: boxed_row {
: button {
// Put code for the Okay button here
}
: button {
// Put code for the Cancel button here
}
}
}
- 145 -
Let's copy in the code for the header and all of the controls above from the "Controls" section of this tutorial. I'll show them in
red. Notice the key names and labels had to be changed.
SAMPLE4 : dialog {
label = "Sample Dialog Box Routine - Part 4";
: column {
: boxed_row {
: popup_list {
key = "mylist1";
label = "Select Item";
fixed_width_font = true;
width = 30;
value = "";
}
: popup_list {
key = "mylist2";
label = "Select Item";
fixed_width_font = true;
width = 30;
value = "";
}
}
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE4.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE4()
- 146 -
)
)
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE4.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
C:Sample4
Command:
Now type Sample4 and press enter. If everything went according to plan you should see this on your screen:
Looks good but, there is nothing to choose. Let's add some data to the popup list boxes. We will need two list. We will call
the list for the first popup list box myList1 and the second myList2.
- 147 -
(setq myList2(list "Plastic" "Steel" "Aluminum" "Concrete"))
Alrighty then, we have our list built. All we have to do is put them in the dialog box. We will use the start_list, add_list, and
end_list functions. Start_list tells DCL which popup list box we are going to edit. The identification is made by using the
popup list box KEY. The first popup list box has a key of "mylist1" and the second has a key of "mylist2". So the start_list
function would look like this:
(start_list "mylist1" 3) ; The 3 means we want to delete the old contents and start new.
Next we use the add_list function to tell DCL which list to put in the popup list box. We use the mapcar function to apply
add_list to each member in the list. Our list for the first popup list box is named myList1. So...
Finally we use the end_list function to tell DCL to display the new contents because we are through editing the popup list
box.
(end_list)
(start_list "mylist1" 3)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 3)
(mapcar 'add_list myList2)
(end_list)
Let's add all of this to the AutoLisp program and see what it looks like.
(defun C:SAMPLE4()
(start_list "mylist1" 3)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 3)
(mapcar 'add_list myList2)
(end_list)
- 148 -
;;;--- If an action event occurs, do this function
(action_tile "accept" "(setq ddiag 2)(saveVars)(done_dialog)")
(action_tile "cancel" "(setq ddiag 1)(done_dialog)")
Notice the location of the red lines. We create the list before loading the dialog box. We add the list to the dialog box after it
is loaded with new_dialog and before the action_tile statements. This is the order you should use.
Looking good so far. We need to add the SaveVars function to save the selected items from the popup list boxes when the
Okay button is pressed. Look at the blue text in the Sample4.lsp program above.
Let's steal the saveVars routine from the popup list box control on the "Saving data from the dialog box" page of this tutorial
and modify it. I'll show the modifications in red.
(defun saveVars()
Wow! That was easy. Wait a minute, we have two pop_up list boxes. We will have to create a function out of this or simply
copy this and do it twice. For now, let's just do it twice.
- 149 -
(defun saveVars()
(defun C:SAMPLE4()
(start_list "mylist1" 4)
(mapcar 'add_list myList1)
(end_list)
(start_list "mylist2" 4)
(mapcar 'add_list myList2)
(end_list)
- 150 -
(princ "\n The user pressed Okay!")
)
)
Last item. We need to replace the line in the program: (princ "\n The user pressed Okay!") with something to
modify and display the selected items. Let's do something simple. We will tell the user what was selected out of each popup
list..
;;;--- Inform the user of his selection from the first list
(princ (strcat "\n You chose " myItem1 " from the first popup list box."))
;;;--- Inform the user of his selections from the second list
(princ (strcat "\n You chose " myItem2 " from the second popup list box."))
)
)
Add the above to the file, save it and test it out. Everything working okay?
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE4() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
- 151 -
Part 5 - Radio Buttons
Let's build a working DCL file showing us exactly how to handle radio buttons.
The first thing you need to know about a radio button is how stupid they are. They have no brains. They do not know what
the other radio buttons are doing. You can layout six radio buttons and select everyone of them like they were toggles. That's
not the way we use radio buttons. They are supposed to be smart. They should know what to do if a radio button around them
is selected. They should turn themselves off because only one radio button in a group is supposed to be checked. That's
where radio_column and radio_row come in to play. They are the brains for the radio buttons. They watch all the buttons in
their row or column to make sure only one is turned on. Okay..moving on.
We will build a DCL file containing 4 radio_buttons plus an Okay and Cancel button. The selected item will be displayed
on the screen after the user presses the Okay button.
Layout thoughts: I will place the radio_buttons in a column, (stacked on top of each other). Then I'll put the Okay and
Cancel buttons in a row at the bottom of the dialog box. So...I'll need something like this:
: column {
: radio_column {
// Put code for radio_column here
: radio_column {
// Put code for radio_button 1 here
}
: radio_button {
// Put code for radio_button 2 here
}
: radio_button {
// Put code for radio_button 3 here
}
: radio_button {
// Put code for radio_button 4 here
}
}
: boxed_row {
: button {
// Put code for the Okay button here
}
: button {
// Put code for the Cancel button here
}
}
}
Let's copy in the code for the header and all of the controls above from the "Controls" section of this tutorial. I'll show them in
red. Notice the key names and labels had to be changed.
SAMPLE5 : dialog {
label = "Sample Dialog Box Routine - Part 5";
: column {
: radio_column {
- 152 -
key = "mychoice";
: radio_button {
key = "but1";
label = "Apples";
}
: radio_button {
key = "but2";
label = "Oranges";
}
: radio_button {
key = "but3";
label = "Bananas";
}
: radio_button {
key = "but4";
label = "Lemons";
}
}
: boxed_row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE5.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE5()
- 153 -
(action_tile "cancel" "(setq ddiag 1)(done_dialog)")
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE5.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
C:Sample5
Command:
Now type Sample5 and press enter. If everything went according to plan you should see this on your screen:
That doesn't look very good does it? Let's change the boxed_row into a boxed_column in our DCL file. (See the blue text in
the DCL file above) Make the changes then Save the Sample5.DCL file. No need to load the autolisp program again, it's
loaded. Just run the Sample5 program again. Now it should look like this:
- 154 -
It still doesn't look right. It's our label "Sample Dialog Box Routine - Part 5" that is causing the problem. Let's shorten it to
"SDBR - Part 5" and try it again:
Looks better!
Looking good so far. We need to add the SaveVars function to save the selected items from the radio_column when the
Okay button is pressed. Look at the blue text in the Sample5.lsp program above.
Let's steal the saveVars routine from the radio_column control on the "Saving data from the dialog box" page of this tutorial
and modify it. I'll show the modifications in red.
We can do this two different ways. We can check each radio_button to find out which one is on or we can check the entire
column of radio_buttons by getting the value of the radio_column.
(defun saveVars()
- 155 -
(defun saveVars()
Wow! That was easy. So...Which one do we use? For this tutorial, let's use both. Why not?
(defun saveVars()
Add this to the original Sample5.lsp program and we should have something that looks like this:
(defun saveVars()
(defun C:SAMPLE5()
- 156 -
)
Last item. We need to replace the line in the program: (princ "\n The user pressed Okay!") with something to
display the selected item.
;;;--- Inform the user of his selection using the radio_column data
(princ "\n Using Radio_column data...You chose ")
(cond
((= myChoice "but1")(princ "Apples!"))
((= myChoice "but2")(princ "Oranges!"))
((= myChoice "but3")(princ "Bananas!"))
((= myChoice "but4")(princ "Lemons!"))
)
;;;--- Inform the user of his selection using the radio_buttons data
(princ "\n Using Radio_buttons data...You chose ")
(cond
((= Choice1 1)(princ "Apples!"))
((= Choice2 1)(princ "Oranges!"))
((= Choice3 1)(princ "Bananas!"))
((= Choice4 1)(princ "Lemons!"))
)
)
)
Add the above to the autolisp file, save it and test it out. Everything working okay?
- 157 -
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE5() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
Let's build a working DCL file showing us exactly how to handle text and toggles.
We will build a DCL file containing 4 toggles, one text, plus a Cancel button. The selected item will be displayed on the
screen in the text control.
Layout thoughts: I will place the text control on the top of the box. Then I'll put the toggles in a column, (stacked on top of
each other). Last, I'll put the Cancel button at the bottom of the dialog box. So...I'll need something like this:
: column {
: column {
: text {
// Put code for text here
}
}
: boxed_column {
- 158 -
: toggle {
// Put code for toggle 1 here
}
: toggle {
// Put code for toggle 2 here
}
: toggle {
// Put code for toggle 3 here
}
: toggle {
// Put code for toggle 4 here
}
}
: boxed_row {
: button {
// Put code for the Cancel button here
}
}
}
Let's copy in the code for the header and all of the controls above from the "Controls" section of this tutorial. I'll show the
changes that needed to be made in red. Notice the key names and labels had to be changed.
SAMPLE6 : dialog {
label = "Sample Dialog Box Routine - Part 6";
: column {
: column {
: text {
key = "text1";
value = "Nothing selected.";
}
}
: boxed_column {
label = "Choose your lucky charms:";
: toggle {
key = "tog1";
label = "Hearts";
value = "0";
}
: toggle {
key = "tog2";
label = "Moons";
value = "0";
}
: toggle {
key = "tog3";
label = "Stars";
value = "0";
}
: toggle {
key = "tog4";
label = "Clovers";
value = "0";
}
- 159 -
}
: boxed_row {
: button {
key = "cancel";
label = "Cancel";
is_default = true;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE6.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE6()
Remove everything listed in orange above. We do not need an Okay button. Thus we do not need to check to see if the user
pressed Okay or Cancel. We also do not need the SaveVars routine in this program. Remove the orange items so your
program looks like the one below.
- 160 -
(defun C:SAMPLE6()
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE6.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
C:Sample6
Command:
Now type Sample6 and press enter. If everything went according to plan you should see this on your screen:
- 161 -
Doesn't look right. The dialog box is too wide. It's our label "Sample Dialog Box Routine - Part 6" that is causing the
problem. I would shorten it but, my text control will need the room if everything is selected. I'll have to display "Hearts
Moons Stars Clovers" all on one line. I'll leave it the way it is.
Notice you can select and deselect items without a problem but the text control doesn't change. We need to add the
action_tiles and function to accomplish this.
First let's write a routine to check each toggle and build a string representing all of the selected items.
(defun chkToggle()
Alrighty then. I used the get_tile function to get the value of each toggle. I used the atoi function to convert that data from
a string to an integer. (I could have left it a string and checked to see if tog1 equalled "1" instead of the number 1.) I set the
variable myStr to an empty string and then appended all the checked toggle labels to it. I then changed the value of the text
control by using the set_tile function.
Add this to the top of your autolisp program and save it.
The last step is to add the action calls to the AutoLisp program. We need one action call per toggle switch. Each action call
should run the chkToggle function we just created.
Let's add this to the AutoLisp program. I'll show the new chkToggle function and the action calls in red. It should look like
this:
(defun chkToggle()
- 162 -
(setq tog1(atoi(get_tile "tog1"))) // 0 = not chosen 1 = chosen
(setq tog2(atoi(get_tile "tog2"))) // 0 = not chosen 1 = chosen
(setq tog3(atoi(get_tile "tog3"))) // 0 = not chosen 1 = chosen
(setq tog4(atoi(get_tile "tog4"))) // 0 = not chosen 1 = chosen
(defun C:SAMPLE6()
- 163 -
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE6() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
PUTTING ALL TOGETHER
Now it is time to take all of the parts and pieces and put them together. Let's build a working DCL file that will let us see
most of the things we've learned and yet not get to deep into autolisp.
Lets draw a polygon or circle, on a selected layer with the option to save the settings last used for defaults next time we run
the program. If polygon is chosen, ask for the number of sides. So....we will need a list_box to hold all of the available layer
names. We will need a radio_column to select a circle or polylgon. We will need an popup_list to hold the number of sides
for the polygon. We will need a toggle to check the "Save Settings" option. And last, we will need an Okay and Cancel
button.
Layout thoughts: The list will need to have several layers showing so the user won't have to scroll forever. So, it will
probably be the tallest item on the dialog box. I'll put it in a column by itself. I'll try to fit the rest of the items in a column
beside it. Then I'll put the Okay and Cancel buttons at the bottom in a row. So...I'll need something like this:
: column {
: row {
: boxed_column {
: radio_column {
: radio_button {
- 164 -
: row {
: list_box {
Let's copy in the code for the header and all of the controls above. I'll show them in red. Notice the key names and labels had
to be changed.
SAMPLE7 : dialog {
label = "Sample Dialog Box Routine - Part 7";
: column {
: row {
: boxed_column {
: radio_column {
key = "radios";
: radio_button {
label = "Draw Circle";
key = "drawcir";
value = "1";
}
: radio_button {
label = "Draw Polygon";
key = "drawpol";
value = "0";
}
}
: popup_list {
key = "numsides";
label = "Number of Sides";
width = 25;
fixed_width_font = true;
}
: toggle {
key = "saveset";
label = "Save settings";
}
}
: row {
: list_box {
label ="Select Layer";
- 165 -
key = "layerList";
height = 5;
width = 15;
multiple_select = false;
fixed_width_font = true;
value = "";
}
}
}
: row {
: button {
key = "accept";
label = " Okay ";
is_default = true;
}
: button {
key = "cancel";
label = " Cancel ";
is_default = false;
is_cancel = true;
}
}
}
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE.DCL Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Next we will get a copy of the AutoLisp model and revise it. All new code is shown in red.
(defun C:SAMPLE7()
- 166 -
)
Right click and copy the above. Open NotePad and paste it. Save the file as SAMPLE7.LSP Be sure to change the
"Save as Type" drop down box to "All Files" before saving it or it will put a ".txt" extension on the file name. Save this
file somewhere in the AutoCAD search path.
Let's load the program and see what the DCL file looks like. On the command line type this:
C:Sample7
Command:
Now type Sample and press enter. If everything went according to plan you should see this on your screen:
Notice there are no items in either list. We haven't added that part yet. Also notice the Draw Circle is selected. We did that
in the DCL file. We set the value of that radio_button to "1". We did not set the value of the toggle, so the default is
unchecked.
If you push the Cancel button the program will exit normally. If you press the Okay button an error will occur because we
have not defined the saveVars routine.
We have two things left to do. First we need to build the list for the popup_list control and the list_box control. Then we
need to add the list to the dialog box.
Instead of building a function to get all of the layer names into a list, let's just build the list manually to keep things simple.
Okay...you talked me into it. I'll do it both ways and you can use the one you want. I'm going to use the short version for this
tutorial.
Long way:
- 167 -
(setq layr(tblnext "LAYER" T))
Short Way:
We need to add these lines to our autolisp program. Add it just below the new_dialog call and above the action_tile
statements.
We now need to upload the list into the dialog box. So put these lines just below the lines you just added.
(defun C:SAMPLE7()
- 168 -
(mapcar 'add_list numSides)
(end_list)
Next, let's build the SaveVars routine. We will do this by starting with the saveVars routine in the AutoLisp Model, then copy
and paste from the "Save Data from Dialog Box" section of this tutorial. We will then modify the names to match the key
names in the DCL file. I also changed a variable name to numStr to be used later to set the default settings. More on that
later. I'll show you the changes I made in red:
(defun saveVars()
;;;--- If the index of the selected item is not "" then something was selected
(if(/= sStr "")
(progn
- 169 -
;;;--- Something is selected, so convert from string to integer
(setq sIndex(atoi sStr))
Note: Since we did not specify a default value for the list_box, layerName will be set to nil if the user does not select a layer
from the list before hitting the Okay button.
Save, Load, and Run. Check the values after pressing the Okay button by typing an exclamation point and then the variable
name on the command line. Examples: To get the layer name type !layerName and press enter. To get the value of the
toggle type !saveSet and press enter. Everything working okay? Alrighty then...let's move on.
The only work left on the dialog box is to disable the "Number of Sides" popup_list when a circle is selected. Let's add two
action_tiles on both of the radio_buttons.
This will send a parameter of 1 to the toggleRadio function if the Circle is selected. It will send a parameter of 2 to the
toggleRadio function if the Polygon is selected. What the hell is a toggleRadio function?
(defun toggleRadio(a)
;else
(mode_tile "numsides" 0) ;enable
)
)
Since our default for the radio buttons is to draw a circle, the numSides popup_list should be disabled before the dialog box
starts. So just before the action_tile statements we need to add this line:
(mode_tile "numsides" 1)
- 170 -
The numSides popup_list is disabled when the circle is selected. Enabled when polygon is selected. Cool.
Here is the AutoLisp program after the lines above were added:
(defun saveVars()
(setq radios(get_tile "radios"))
;;;--- If the index of the selected item is not "" then something was selected
(if(/= sStr "")
(progn
- 171 -
;;;--- And set the name of the selected item to nil
(setq layerName nil)
)
)
)
(defun toggleRadio(a)
;if circle is selected
(if(= a 1)
(mode_tile "numsides" 1) ;disable
;else
(mode_tile "numsides" 0) ;enable
)
)
(defun C:SAMPLE7()
(mode_tile "numsides" 1)
- 172 -
)
Now the only thing remaining is to do something when the user presses the okay button. We have all of the dialog box data
stored in variable names....
Variable Name DCL Control Item Action Key Type of Data Stored
radios Radio_Column "radios" String [ Action Key Name]
numSides Popup_List "numsides" Integer [ Number of sides]
saveSet Toggle "saveset" Integer [ 0 or 1 ]
layerName List "layerlist" String [ Name of Layer]
So now all we have to do is write the AutoLisp code inside the "Okay button was pressed" IF statement. (Where the blue
line is above.)
Now we will replace the blue line above: (princ "\n \n ...SAMPLE Complete!") with new code.
Add the above to your program and save it. Test it out. Everything working okay?
And finally all we have left is the code for the default settings. Since everything in the dialog box is a string, why don't we
save all of the data as strings. Lucky for us, AutoDesk included 15 setvars for us to store data in. USERR1 thru USERR5 is
used to store real numbers. USERI1 thru USERI5 is used to store integers. USERS1 thru USERS5 are used to store strings.
We will use the system variables USERS1 thru USERS4 to save our data since we are saving the data as strings. All of these
setvars are stored inside the drawing file. They will be there everytime you open your drawing. How convenient! So let's get
to it.
- 173 -
The second variable to store is NUMSIDES and it is an integer representing the number of sides the polygon should have. I
want to store the index of the selected item in the list not the actual number of sides. We saved the index as a variable named
NUMSTR when we ran the saveVars routine. So...
The third variable to save is SAVESET. It is stored as an integer. We will have to convert it to a string to save it.
The fourth and final variable to save is LAYERNAME. It is stored as a string. I want to save the index of the selected item.
We saved that earlier with the saveVars routine in a variable named sSTR.
The last thing we have to do is check for the default settings and load them if they exist.
(defun saveVars()
;;;--- If the index of the selected item is not "" then something was selected
(if(/= sStr "")
(progn
(defun toggleRadio(a)
;if circle is selected
(if(= a 1)
- 174 -
(mode_tile "numsides" 1) ;disable
;else
(mode_tile "numsides" 0) ;enable
)
)
(defun C:SAMPLE7()
- 175 -
(princ "\n \n ...SAMPLE7 Cancelled. \n ")
)
)
)
)
)
When you get your program tested and everything is working, move the blue line above, [ (defun C:SAMPLE7() ] all the way
to the top of the file. This will make all of your variables local and will reset them all to nil when the program ends.
- 176 -