Often you want to execute a procedure based on a certain condition. One way of
doing this is with the IF statement, which does one thing if a statement is true and
another thing if it is false. In other words, the operation is conditioned on the truth
of a certain statement.
Looping is an important part of programming. Frequently you want to execute a
procedure over and over until the routine has finished operating on all the target
objects or items. Looping sets up the condition that determines when the operation
starts, the number of objects upon which the routine operates, and when the operation
ends.
Conditional structures
Conditional structures enable program flow to be determined based on the outcome
of a given decision. These decisions result in the return of either T, meaning
true, or nil, meaning false. To try out the following statements, type them in the
Visual LISP Console window. For instance, for the statement
133 trang |
Chia sẻ: tlsuongmuoi | Lượt xem: 2158 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Programming AutoCAD, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
1146 Part VII ✦ Programming AutoCAD
6. Type (cdr endpt) ↵. AutoLISP returns (2.0 1.4).
7. Type (nth 1 endpt) ↵. AutoLISP returns 2.0.
Do not save your drawing.
Setting Conditions
Often you want to execute a procedure based on a certain condition. One way of
doing this is with the IF statement, which does one thing if a statement is true and
another thing if it is false. In other words, the operation is conditioned on the truth
of a certain statement.
Looping is an important part of programming. Frequently you want to execute a
procedure over and over until the routine has finished operating on all the target
objects or items. Looping sets up the condition that determines when the operation
starts, the number of objects upon which the routine operates, and when the opera-
tion ends.
Conditional structures
Conditional structures enable program flow to be determined based on the out-
come of a given decision. These decisions result in the return of either T, meaning
true, or nil, meaning false. To try out the following statements, type them in the
Visual LISP Console window. For instance, for the statement
(< 3 5)
T
AutoLISP returns T for true, having determined that 3 is less than 5. For the statement
(> 3 5)
nil
AutoLISP returns nil for false, having determined that 3 is not greater than 5. For
the statement
(= 3 5)
nil
AutoLISP returns nil. Here we have determined that 3 is not equal to 5. Because
these statements return either T or nil, you can use the IF statement. The general
syntax of the IF statement is (if conditional-test if-true if-false).
Say you want to find circles whose radius is less than 0.25. Here’s a sample IF state-
ment. In this example, radius is a variable that has been previously set.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1146
1147Chapter 35 ✦ Exploring AutoLISP Further
(if (< radius .25)
(princ “The radius is less than .25”)
(princ “The radius is not less than .25”)
)
The conditional test is (< radius .25). The if-true statement is (princ “The
radius is less than .25”). The if-false statement is (princ “The radius is
not less than .25”). This IF statement is equivalent to saying, “If the radius is
less than .25, print ‘The radius is less than .25’ but if not, print ‘The radius is not
less than .25.’”
You can leave out the if-false statement. Then AutoLISP executes the if-true state-
ment if the conditional statement is true and does nothing if it is false, and contin-
ues to the rest of the program. In the following exercise, you see both types of IF
statements, one nested inside the other.
Step-by-Step: Using the IF Statement
1. Start a new drawing in AutoCAD using the acad.dwt template.
2. Open Visual LISP, start a new file, and type the following:
(defun c:compare2three (/ entered_num)
(setq entered_num (getint “\nEnter an integer: “))
(if (< entered_num 3)
(princ “\nThe entered number is less than 3.”)
(if (= entered_num 3)
(princ “\nThe entered number is equal to 3.”)
(princ “\nThe entered number is greater than 3.”)
)
)
(princ)
)
The GETINT function gets an integer from the user and is covered later in this
chapter. The \n before the Enter an integer: prompt starts a new line; it is
similar to using (terpri). Using (princ) at the end of a routine is also cov-
ered later in this chapter.
3. Choose Check Edit Window. If you see any error message in the Build Output
window, check your typing, make any necessary correction, and try again.
4. Save the file as ab35-02.lsp in a folder that is in the support file search path,
or in AutoCAD 2004\Support\.
5. Choose Load active edit window and then choose Activate AutoCAD.
6. To try out the IF statement, type compare2three ↵. At the Enter an inte-
ger: prompt, type 5 ↵. AutoCAD displays: The entered number is
greater than 3.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1147
1148 Part VII ✦ Programming AutoCAD
7. Repeat the COMPARE2THREE command. At the Enter an integer: prompt,
type 3 ↵. AutoCAD displays: The entered number is equal to 3.
8. Repeat the COMPARE2THREE command. At the Enter an integer: com-
mand, type 2 ↵. AutoCAD displays: The entered number is less than 3.
Do not save your drawing.
Loop structures
Looping provides the capability to execute a step or a number of steps a given
number of times based on an evaluation you make in your application. One way to
do this is with the WHILE function.
The format of the WHILE function is:
(while conditional-test-if-true
then-perform-the-following-code-until-the condition-is-
false)
One method that can be useful with a WHILE conditional-test-if-true is to include a
counter for the WHILE expression. A counter counts how many times an operation is
executed. You can then end the operation when the counter reaches a certain num-
ber. To create a counter, set a variable (perhaps named “counter”) to the number at
which you want to start. Then write the code for one pass through the operation.
Then set the counter to the next higher number using an expression, such as the
following:
(setq counter (+ 1 counter))
The routine then loops back over the operation until the counter reaches the value
you set.
Here’s a simple example:
(defun c:process (/ counter)
(setq counter 1)
(while (< counter 6)
(princ “Processing number “)
(princ counter)
(terpri)
(setq counter (+ 1 counter))
)
)
In this example, the process function starts by setting the variable counter to 1.
Then you start the WHILE statement and specify that the counter must be less than
6. Within the WHILE statement, you print the text string “Processing number” and
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1148
1149Chapter 35 ✦ Exploring AutoLISP Further
then the value of the counter variable. You use (terpri) so that each text string
starts on a new line. Then you set the counter to the next higher number. Each time
through the WHILE loop, the value of the counter is incremented by 1. Without the
increment statement, line 3 would always evaluate to true and the WHILE loop would
never exit because the counter would always be 1.
If you accidentally program an infinite loop like this, you can stop the execution of
your AutoLISP routine by pressing Esc, Ctrl+Break, or from the Visual LISP menu,
choose Debug ➪ Abort Evaluation.
In the preceding example, the WHILE loop continues as long as the counter is less
than 6. When the counter reaches 6, the routine stops. The WHILE statement returns
the last value of the routine, so AutoCAD prints 6 on the last line. Figure 35-3 shows
the result.
Figure 35-3: The result
of the process function
When using WHILE, you may want to combine several operations under the condi-
tion. The IF function normally evaluates one then expression if the test expression
is true. Suppose you want an IF expression that processes various tasks if the test
condition is true. An IF expression, as previously mentioned, processes a “do-if-true”
and “do-if-false.” Therefore, to process more than one “do-if true” expression, you
need to separate the “do-if-true” processes from the “do-if-false” processes. To
accomplish this, use PROGN (PROGram Nest) to include (or nest) all items you want
executed when the test is true. In general, PROGN evaluates all the statements within
its parentheses and returns the last evaluation as if it were one statement, as the
following example demonstrates.
In the next example, you see the same IF statements used in an earlier example.
However, after the second IF statement, you want your routine to print two lines if
the entered number equals 3. You can do this by enclosing the two lines of code
(plus a terpri) within a PROGN statement:
(defun c:compare2three (/ entered_num)
(setq entered_num (getint “\nEnter an integer: “))
(if (< entered_num 3)
(princ “\nThe entered number is less than 3.”)
(if (= entered_num 3)
(progn
(princ “\nThe entered number is equal to 3.”)
(terpri)
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1149
1150 Part VII ✦ Programming AutoCAD
(princ “\nThis is the one we are looking for.”)
)
(princ “\nThe entered number is greater than 3.”)
)
)
(princ)
)
The file used in the following Step-by-Step exercise on using WHILE, IF, PROGN, and a
counter, ab35-b.lsp, is in the Drawings folder of the AutoCAD 2004 Bible
CD-ROM.
Step-by-Step: Using WHILE, IF, PROGN, and a Counter
1. Open AutoCAD with any new drawing.
2. Open Visual LISP.
3. Click Open File on the Visual LISP Standard toolbar and open ab35-b.lsp
from the AutoCAD 2004 Bible CD-ROM.
4. Save the file as ab35-03.lsp in \AutoCAD 2004\Support or any folder in the
support file search path. Figure 35-4 shows this routine.
Figure 35-4: The print0to10 routine
5. Load ab35-03.lsp. Return to AutoCAD.
6. Type print0to10 ↵. Press F2 to open the AutoCAD Text Window and see the
result, shown in Figure 35-5.
Do not save your drawing.
On the
CD-ROM
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1150
1151Chapter 35 ✦ Exploring AutoLISP Further
Figure 35-5: The result of the
print0to10 function
Managing Drawing Objects
The real power of AutoLISP is in manipulating drawing objects. This section reveals
how many of the AutoLISP routines perform their magic. You can get information
about an object and then use that information to change the object. You can also
create selection sets of objects.
Getting information about an object
Every object in the AutoCAD database has an entity name. This name enables you
to reference that object anywhere in your AutoLISP application. To see an example
of an entity name, type the following after starting a new drawing:
(command “_line” “3,3” “5,5” “”)
(entlast)
AutoLISP responds with an Entity name such as .
The numbers will probably differ on your system, but using the information
returned from ENTLAST enables you to programmatically get or set a variety of
options on any given database object by referencing its entity name.
The ENTGET (ENTity GET) function is the key to making modifications to the draw-
ing database. The ENTGET function takes an entity name as its one and only argu-
ment. After drawing the line in the preceding steps, type the following:
(setq myline (entget (entlast)))
AutoLISP responds with:
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1151
1152 Part VII ✦ Programming AutoCAD
((-1 . ) (0 . “LINE”) (330 . <Entity
name: 15ac4f8>)(5 . “2B”) (100 . “AcDbEntity”) (67 . 0)
(410 . “Model”) (8 . “0”)(100 . “AcDbLine”) (10 3.0 3.0 0.0)
(11 5.0 5.0 0.0) (210 0.0 0.0 1.0))
This is a representation of how the line is stored in the AutoCAD drawing database.
AutoLISP returned one large list that contains 12 smaller lists. Each of the smaller
lists is referred to as a group indexed by the first element. The entity name is in
group –1. Each of the initial numbers in the small lists represents a different quality
of the line. These numbers are called object group codes. The object group codes
used most often are listed in Table 35-2.
Table 35-2
Commonly Used AutoCAD Object Group Codes
Group Code Description
–1 Entity name
0 Entity type
1 Text value
8 Layer
10 Start point (or center)
11 Endpoint (or alignment point)
38 Elevation
39 Thickness
40 Radius (or height of text)
62 Color
67 Paper space flag
As you can see from the 10, 11, and 40 codes, the meaning of the group codes can
change depending on the type of object it is used for.
You can also use Visual LISP to examine an AutoCAD object. Begin by choosing
View ➪ Browse Drawing Database ➪ Browse Selection. At this point, Visual LISP will
display a pickbox in your drawing so that you can select an object.
After you end object selection, you are returned to Visual LISP where you see the
Inspect dialog box. Select the name of the entity and, while the cursor is over the
selected text, right-click. Choose Inspect to see information about the object. Figure
35-6 shows information about an arc.
Note
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1152
1153Chapter 35 ✦ Exploring AutoLISP Further
Figure 35-6: Getting information
about a drawing object from
Visual LISP
Not all these group codes are present in the line you drew. For instance, group 62
(color) is absent in the list returned by AutoLISP. Every time you draw a line, you
do not explicitly set its color. As a result, it defaults to the current color. In the same
way, AutoLISP does not explicitly set every attribute of every group. In this case,
the color is ByLayer and the current layer is 0. AutoLISP returned (8. “0”) in the
preceding list to signify the line is on layer 0.
There are many other group codes than the ones listed in Table 35-2, and they can
be found in the Visual LISP Help system. From Visual LISP, choose Help ➪ Visual
LISP Help Topics. From the Contents tab, double-click DXF Reference. (Because the
group codes are also used for DXF files, they are found in the DXF Reference.)
Double-click ENTITIES Section. You can then either choose Common Group Codes
for Entities or choose the specific entity you want to look up. In many cases, one
group code can have different meanings depending on the entity in which it
appears. For example, in the list representing the line you drew, group 10 is repre-
sented by (10 3.0 3.0 0.0), which means the start point of the line is at X=3.0, Y=3.0,
Z=0.0. If group 0 were a circle instead, the coordinates of group 10 would specify the
center point of the circle.
To manipulate a given attribute of an object, two important functions are ASSOC and
SUBST:
✦ ASSOC returns a list that finds the entry associated with an item in a list. It
takes two arguments, the item in the list and the list itself. For example, if you
specify the group code (such as 10) as the first argument, it returns the code’s
value (which would be the start point of a line). If a list named myobject con-
tains three groups, as in ((0 . “group 0”) (1 1.0 2.0) (3 4.2 1.5
6.75)), then (assoc 1 myobject) would return (1 1.0 2.0).
✦ SUBST substitutes a value for every occurrence in a list. The SUBST function
takes three arguments. To make the substitution, the first argument specifies
what to substitute with, the second argument specifies what to substitute for,
and the third argument specifies on what list to perform this operation.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1153
1154 Part VII ✦ Programming AutoCAD
To manipulate the start point of your line, first get the start point:
(setq startpt (assoc 10 myline))
AutoLISP responds:
(10 3.0 3.0 0.0)
To modify the start point of your line, use:
(setq new_startpt ‘(10 6.5 1.0 0.0))
(setq myline (subst new_startpt startpt myline))
AutoLISP responds:
((-1 . ) (0 . “LINE”) (330 . <Entity
name: 15ac4f8>)(5 . “2B”) (100 . “AcDbEntity”) (67 . 0)
(410 . “Model”) (8 . “0”)(100 . “AcDbLine”) (10 6.5 1.0 0.0)
(11 5.0 5.0 0.0) (210 0.0 0.0 1.0))
In this case, the new_startpt is substituted for the existing startpt in the object
myline. No changes to the line are yet apparent. To commit the change, you need
the ENTMOD function.
Modifying objects
The key to modifying objects is the ENTMOD (ENTity MODify) function. The list
returned by AutoLISP can be modified and then passed to ENTMOD as an argument to
update the AutoCAD database. Continuing with the example, if you enter:
(entmod myline)
AutoLISP responds:
((-1 . ) (0 . “LINE”) (330 . <Entity
name: 15ac4f8>) (5 .”2B”) (100 . “AcDbEntity”) (67 . 0)
(410 . “Model”) (8 . “0”) (100.”AcDbLine”) (10 6.5 1.0 0.0)
(11 5.0 5.0 0.0) (210 0.0 0.0 1.0))
The AutoCAD database is changed as well, and the start point of your line is now at
X=6.5, Y=1.0, Z=0.0.
Creating selection sets
A selection set is created with the SSGET (Selection Set GET) function. This
prompts the user with the familiar Select objects: prompt. Table 35-3 shows the
commonly used selection set functions.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1154
1155Chapter 35 ✦ Exploring AutoLISP Further
Table 35-3
Common AutoCAD Selection Set Functions
Function Description
SSGET Obtains a selection set from the user.
SSLENGTH Returns the number of objects in a selection set. It takes one
argument, the selection set.
ssname Returns the entity name of a given object in a selection set. It takes
two arguments: the selection set and the number of the object in the
selection set. The first item number is 0, the second is 1, and so on.
You can use a maximum of 256 selection sets at any given time. To release a selec-
tion set back to AutoLISP so that it can be used again, set the selection set to nil—
for example, (setq ss nil).
For example, you can enter the following in a new drawing:
(command “_circle” “3,3” “2”)
nil
(command “_circle” “4,4” “3”)
nil
(command “_line” “7,2” “6,6” “3,4” “5,5” “”)
nil
(setq mysset (ssget))
Select objects: all ↵
5 found
Select objects: ↵
Now mysset is set to the selection set specified by all, which includes the three
lines segments and the two circles. To see what you have in your selection set, type
the following either on the command line or in the Visual LISP Console:
(sslength mysset)
5
You now know that you have five objects in your selection set. The first object is
number 0, and the fifth object is number 4. To see what the first object is, enter the
following:
(ssname mysset 0)
To get the database data on the object, enter:
(entget (ssname mysset 0))
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1155
1156 Part VII ✦ Programming AutoCAD
Visual LISP responds:
((-1 . ) (0 . “LINE”) (330 . <Entity
name: 16014f8>)(5 . “30”) (100 . “AcDbEntity”) (67 . 0) (410 .
“Model”) (8 . “0”)(100 . “AcDbLine”) (10 3.0 4.0 0.0) (11 5.0
5.0 0.0) (210 0.0 0.0 1.0))
By stepping through each of the entity names returned by SSNAME from 0 to 4, you
can manipulate each of the objects in the selection set.
Step-by-Step: Creating Selection Sets
1. Start a new drawing using the acad.dwt template and type the following in a
new file in the Visual LISP edit window. Save it as ab35-04.lsp in your
\AutoCAD 2004\Support folder or any folder in the support file search path.
(defun c:listsset (/ mysset counter)
(setq mysset (ssget))
(setq counter 0)
(while (< counter (sslength mysset))
(terpri)
(princ (cdr (assoc 0 (entget (ssname mysset counter)))))
(setq counter (+ counter 1))
)
(princ)
)
2. Load ab35-04.lsp.
3. Activate AutoCAD and draw any number of objects on-screen — at least two
different types of objects.
4. Type listsset ↵. AutoCAD prompts you to select objects (because of the ssget
function).
5. Select all the objects in your drawing. The routine prints the type of each
object you selected. (Press F2 to open the AutoCAD Text window to see the
entire results.) Figure 35-7 shows the result. Of course, your result will be dif-
ferent because you probably drew different types of objects.
Figure 35-7: One possible result of the listsset routine
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1156
1157Chapter 35 ✦ Exploring AutoLISP Further
Here’s how this routine works:
✦ Line 1 creates a function and declares two variables, mysset and counter.
✦ Line 2 sets the mysset variable equal to the selection set that the user pro-
vides using SSGET.
✦ Line 3 sets the counter variable to zero, because selection sets work on a
zero-based index (the first item is zero).
✦ Line 4 starts a WHILE loop. Working from the innermost set of parentheses, first
you obtain the number of objects in the mysset selection set, using SSLENGTH.
Then you specify that the WHILE loop will continue as long as the counter is
less than the number of objects in the mysset selection set. In other words,
when the routine has cycled through all the objects in the selection set, it
stops.
✦ Line 5 uses TERPRI to start a new line before printing out the list of objects.
✦ Line 6 prints the list. Working from the innermost set of parentheses, first the
routine obtains the name of the object in the mysset selection set whose
number is equal to the variable counter. Then the routine gets that object
using ENTGET. Then the routine gets the name of that object using ASSOC with
the 0 group code. The result is a dotted pair list whose second item is the
name of the object. The CDR function eliminates the first item in the dotted
pair list, leaving just the name of the object, which is what you want. The rou-
tine then prints the result.
✦ Line 7 sets the counter up one to repeat the WHILE loop for the next object.
✦ Line 8 closes the WHILE loop.
✦ Line 9 exits the routine quietly. I discuss exiting quietly later in the chapter.
✦ Line 10 closes the entire function.
Getting Input from the User
The course of your AutoLISP routines may often depend on user input. To satisfy
this need, AutoCAD has a family of functions prefaced with the word “GET.” You
have seen GETVAR for obtaining system variable information. Table 35-4 shows some
other useful GET functions.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1157
1158 Part VII ✦ Programming AutoCAD
Table 35-4
Basic User Input Functions
Function Description
GETDIST Returns the distance between two points
GETINT Returns an integer
GETREAL Returns a real number (which can be a non-integer, negative, and so on)
GETSTRING Returns a text string
Within the COMMAND function, you can use the PAUSE function to pause the operation
of the routine and enable user input, such as picking a point or typing a value. For
example, the expression (command “circle” pause “3”) pauses to let the user
specify a center point and then creates a circle with a radius of 3.
Notice the function ENTSEL in the next exercise. This is a type of shorthand for SSGET.
Use it when you want to limit the user to selecting a single object. ENTSEL returns an
entity name and the coordinates of your pick point in a dotted-pair list. Therefore,
you can use CAR before ENTSEL to get the entity name for ENTGET.
There is also a new argument for getstring, T. If you use this argument and it is not
nil, users can place spaces in the input. Using this argument enables users to type
a text value that includes more than one word. Without the “T”, AutoLISP would
interpret a space as equivalent to pressing Enter.
Step-by-Step: Getting User Input
1. Start a new drawing using the acad.dwt template.
2. Start Visual LISP, open a new file, and enter the following routine. Save it as
ab35-05.lsp in AutoCAD2004\Support or a folder that you have added to
the support file search path.
(defun c:chgmytext (/ src_object new_ht new_str)
(setq src_object (entget (car (entsel))))
(setq new_ht (getreal “\nWhat is the new height? “))
(setq new_str (getstring T “\nWhat is the new text value? “))
(setq src_object
(subst (cons 40 new_ht) (assoc 40 src_object ) src_object)
)
(setq src_object
(subst (cons 1 new_str) (assoc 1 src_object) src_object)
)
(entmod src_object)
(princ)
)
3. Choose Format Edit Window from the Tools toolbar.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1158
1159Chapter 35 ✦ Exploring AutoLISP Further
4. Load ab35-05.lsp.
5. Create some text using either the DTEXT or the TEXT command and run
chgmytext on it. AutoCAD changes the text object’s height and content to the
values you input in response to the prompts.
Do not save your drawing.
Here’s how the routine works:
✦ Line 1 defines the function and declares the three variables.
✦ Line 2 uses ENTSEL to let the user select one object. As explained earlier, CAR
enables you to get just the entity name. ENTGET gets this entity name so that
you can modify it. Finally, this line sets the resulting entity name equal to the
variable src_object.
✦ Line 3 prompts the user for a new height for the text and sets the entered
number equal to the new_ht variable.
✦ Line 4 prompts the user for a new text value (a new string) and sets the
entered text value equal to the new_str variable.
✦ Line 5 starts the process of substituting the new values for the old values.
Here the routine starts to set the new value for src_object.
✦ Line 6 uses SUBST to substitute the new text height for the old text height for
the object src_object. (Group code 40 represents text height.)
✦ Line 7 closes the SETQ function.
✦ Line 8 is the same as line 5. You will be repeating the process of lines 5
through 7 for the new text (string) value.
✦ Line 9 uses SUBST to substitute the new text value for the old text value.
(Group code 1 represents the text value.)
✦ Line 10 closes the SETQ function.
✦ Line 11 uses ENTMOD on src_object to make the change in the drawing
database.
✦ Line 12 exits quietly.
✦ Line 13 closes the entire function.
Putting on the Finishing Touches
You have a number of finishing touches that you should add to a routine before you
can call it complete. All AutoLISP expressions return a value and the last expression
returns its value on the command line. You have noticed the PRINC function at the
end of several routines. PRINC returns a blank line and therefore ensures that no
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1159
1160 Part VII ✦ Programming AutoCAD
extraneous evaluation return values are echoed on the command line. Using the
PRINC function in this way is called exiting cleanly or quietly.
Most of the AutoLISP applications covered thus far do not include much in the way
of error handling. A new function called EQUAL is used on line 4 of the final routine in
the next Step-by-Step exercise. EQUAL is different than = in that EQUAL returns true
only if two expressions are equal (each of the two objects tested for equality are
evaluated before checking if they are equal). A simple rule of thumb is to use EQUAL
for list comparisons and = for numeric comparisons.
In the routine in the next Step-by-Step exercise, if you select an object that is not a
text object, the program jumps to line 18 and prints the error message You must
select a text object.
A similar type of error handling is to enclose the selection of objects in an IF func-
tion. The IF function’s test condition has to exist for the IF function to work.
Therefore, if your user doesn’t select an object, the test evaluates as false. You can
place an error message as the if-false part of the IF function to catch this type of
error.
This type of error handling is crucial to making your AutoLISP programs look fin-
ished and function properly.
Another way to finish off your routine is to add comments. Start with a comment at
the beginning that states the purpose and function of the routine. This helps others
understand what the routine does and can help you as well when you look at the
routine again a few months later! Comments are prefaced with a semicolon.
Then continue to make comments throughout your code. A lack of comments can
make even the most useful code useless. Most professional programmers fully com-
ment and document a function’s purpose and behavior by placing comments within
the code.
Visual LISP supports several commenting styles. When you click Format Edit
Window on the Tools toolbar, Visual LISP uses these styles to automatically format
your code. Here’s how they work:
✦ ;;; Triple semicolon: When you type a comment with three semicolons, Visual
LISP places the comment at the left margin. A triple semicolon comment is
good for the beginning of your routine to describe its overall purpose and
what it does.
✦ ;; Double semicolon: Visual LISP indents a comment with two semicolons at
the current nesting level, flush with the next level of parentheses. Use this
type of comment to explain the next line or lines of code.
✦ ; Single semicolon: Visual LISP indents a comment with one semicolon by 40
spaces, by default. Choose Tools ➪ Environment Options ➪ Visual LISP Format
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1160
1161Chapter 35 ✦ Exploring AutoLISP Further
Options to open the Format options dialog box, shown in Figure 35-8 and
change this value if you want. Use this type of comment for running com-
ments at the right side of your code. Because they are indented, they stand
out from the body of your code.
✦ ;| |; Inline comment: Place an inline comment within any line of code so that
it has code both before and after it. An inline comment is formatted as shown
here: ;|This is a comment|;. You use the pipe symbol (usually over the
backslash) along with the semicolon. Use an inline comment to explain a
small section of code within a line or to span comments over several lines
without adding a semicolon before each line.
✦ ;_ End-of-line comment: Place an end-of-line comment at the end of any line of
code. An end of line comment is formatted as shown here: ;_ This is an
end of line comment. You use the underscore symbol along with the semi-
colon. Use an end-of-line comment to explain which function to which the
closing parenthesis is matched. This is especially useful for conditional func-
tions, such as one closing parenthesis can be several lines from the opening
parenthesis.
Figure 35-8: Use the Format options dialog
box to format margins and indentation in
the Visual LISP editor.
See the sidebar “Using AutoLISP to match properties” for several examples of com-
ment styles.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1161
1162 Part VII ✦ Programming AutoCAD
Using AutoLISP to match properties
This routine modifies the layer of objects to match the layer of one other object. This pow-
erful routine was a mainstay of many AutoCAD users before the advent of the MATCHPROP
command introduced in AutoCAD R14. The general method used here for matching layers
can be used to change any properties you wish on any AutoCAD object.
;;;Matches the layer of one selected source object in any number
of destination objects.
(defun c:matchlayer (/ src_object mysset counter
cur_ent_ent_layer)
(princ “\n*** Select Source object to match ***”) ; prompt the
user
(if (setq src_object (car (entsel))) ; select the object
(progn
(setq src_layer (assoc 8 (entget src_object))) ; get the
object’s layer
;; prompt the user
(princ “\n*** Select Destination objects to match layer
***”)
;; collect some objects using ssget
(if (setq mysset (ssget)) ; verify the user selected
something
(progn ; if the user selected some items do the
following
(setq counter 0)
(while (< counter (sslength mysset))
(setq cur_ent (entget (ssname mysset counter)))
(setq ent_layer (assoc 8 cur_ent))
(entmod (subst src_layer ent_layer cur_ent))
(setq counter (+ counter 1))
)
)
(princ “\nYou did not select any items”) ; prompt the user
) ;_end of if verification
) ;_end of progn for the selection of object to match
;; prompt the user they did not select a source object
(princ “\nSorry you did not select an Object”)
) ;_end of if
(princ)
) ;_end of function c:matchlayer
This routine first gets the name of the selected object with (car (entsel)). It determines
its layer by using ENTGET on the object name and using ASSOC with the 8 group code. Then it
gets a selection set of the Destination objects. It creates a loop that cycles through these
objects, gets their layers (the ent_layer variable), and uses ENTMOD and SUBST to change
the object’s current layer to the source object’s layer.
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1162
1163Chapter 35 ✦ Exploring AutoLISP Further
Step-by-Step: Putting on the Finishing Touches
1. Load the application completed in the previous Step-by-Step exercise. If you
didn’t do the previous exercise, enter it from that exercise in Visual LISP’s edit
window and save it as ab35-05.lsp in \AutoCAD 2004\Support or a folder
that you have added to the support file search path. Then load it with any
drawing open in AutoCAD.
2. Now run chgmytext and choose an object that is not a text object (such as a
circle) in response to the Select object: prompt. Answer the prompts for
new height and new text value.
If you have done this to a circle, you see its radius change to match the value
you specified to be the new text height. This is definitely not what you
intended when writing this program.
3. Modify the program so that it reads this way and save it as ab35-06.lsp:
;;;modifies text height and content (value)
(defun c:chgmytext (/ src_object new_ht new_str)
(terpri)
(setq src_object (entget (car (entsel))))
(if (equal (assoc 0 src_object) ‘(0 . “TEXT”))
(progn
(princ “What is the new height for the text?
“)
(setq new_ht (getreal))
(princ “What is the new text value? “)
(setq new_str (getstring))
(setq src_object
(subst (cons 40 new_ht) (assoc 40
src_object) src_object)
)
(setq src_object
(subst (cons 1 new_str) (assoc 1
src_object)src_object)
)
(entmod src_object)
)
(princ “You must select a text object.”)
)
(princ)
)
4. Load ab35-06.lsp. Start chgmytext and try out the routine again with a cir-
cle or other non-text object.
Do not save your drawing.
Each selection of objects (the source object and the destination objects) is enclosed in an
IF function whose if-false statement prints an error message telling users that they did not
select an object (or objects).
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1163
1164 Part VII ✦ Programming AutoCAD
Summary
In this chapter, you read how to create variables, create AutoLISP functions, and
work with AutoCAD commands and system variables. You extended AutoLISP’s
power by using lists and looping. This chapter explained how to modify and get
information about drawing objects. You also read how to create selection sets.
Using these techniques, along with user input, you can automate the modification
of objects in your drawing to suit your needs.
You should always finish off your AutoLISP routines by adding some error handling,
making sure the routine exits quietly, and adding helpful comments about the rou-
tine’s function.
In the next chapter, you read some of the more advanced features of Visual LISP.
✦ ✦ ✦
43 539922 Ch35.qxd 5/2/03 9:44 AM Page 1164
Exploring
Advanced
AutoLISP Topics
This chapter introduces you to a few helpful advancedAutoLISP topics, including local and global variables,
ActiveX, and debugging.
Understanding Local
and Global Variables
In this section, you read how local and global variables are
accessed within a function, as well as some common syntax.
You also discover what can happen when global variables are
not properly documented.
Chapter 35 explained that a variable is a symbolic name that
can be operated on in a given program. An important part of
using variables is being able to assign values to them. There
are two types of variables — global and local.
A global variable is exposed, or available, to all AutoLISP func-
tions that you have loaded into your drawing. A global vari-
able retains its value after the program that defined it is
finished. You use a global variable when you want its value to
be available across an entire project as opposed to just one
function within a project, to retain a fixed value that might be
used and assigned by different functions, or for debugging.
Any variable you do not specifically define as a local variable
is a global variable.
36C H A P T E R
✦ ✦ ✦ ✦
In This Chapter
Understanding local
and global variables
Working with Visual
LISP ActiveX functions
Debugging code
Using the Error trace
window
Using the Watch
window
✦ ✦ ✦ ✦
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1165
1166 Part VII ✦ Programming AutoCAD
A local variable is temporarily assigned a value during a function’s execution. After
the function completes executing, the local variable value is discarded. AutoLISP
can now use the memory that was taken up by that local variable. You use a local
variable when you want to be sure you don’t have variable values floating around
interfering with other functions. Local variables are also easier to debug because
they only affect the code within their function. In general, most of your variables
should be local. You create a local variable and declare it in the DEFUN statement
after the slash and a space, as in this example:
(defun list-objects ( / counter sset)...
Global variables can be tricky. They can easily cause bugs: Their values persist and
can be hard to debug because the values are hard to find. A common syntax for
global variables is to prefix and suffix the variable with an asterisk as in
*aGlobal*. In this way, you can easily identify global variables in your code. Keep
your use of global variables to a minimum and carefully document those you do
use. Failure to follow these simple rules could result in undesirable and difficult-
to-trace bugs.
Step-by-Step: Using Local and Global Variables
1. Start a new drawing using the acad.dwt template. You should not have any
other drawing open.
2. Open the Visual LISP Editor.
3. In the Console window, type the following line and then press Ctrl+Enter. You
use Ctrl+Enter in the Console window to enter in code of more than one line.
This line declares one local variable:
(defun local-variable (/ var1)
4. Type the second line of code in the Console window as follows:
(setq var1 “I’m local”)) ↵
This sets the local variable to the string you typed. The Console returns the
name of the function.
LOCAL-VARIABLE
5. Before you test this function, you can check out the current value of the local
variable: var1. Type the following in the Visual LISP Console:
var1 ↵
The Console returns
nil
As you can see, the value is nil.
Caution
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1166
1167Chapter 36 ✦ Exploring Advanced AutoLISP Topics
6. Test the local-variable function to check the value it sets to the var1 variable.
In the Console, type (local-variable) ↵. The Console returns:
“I’m local”
You now know that the local-variable function definitely assigns the value of
“I’m local” to the variable var1.
7. To create a global variable, type the following in the Console.
(setq var1 “I’m global”) ↵
The Console returns “I’m global” so you know that the value of var1 is
now “I’m global”.
8. Test the local variable again by typing (local-variable) in the Console. The
Console returns “I’m local” because it executes the local-variable function.
9. Test the variable var1 to see what its value is now. In the Console, type var1
↵. The Console returns “I’m global”. The local variable was not retained
when the function used the variable because the variable was local to the
function. However, the global variable’s value persisted.
Working with Visual LISP ActiveX Functions
ActiveX is an interface that exposes objects to the user, a programmer, or an appli-
cation. AutoLISP supports ActiveX, giving you more information and flexibility in
working with your drawings. You can also use ActiveX to work with objects in other
Windows applications that support ActiveX. ActiveX is a programming interface that
is used within a programming language that supports it. For example, you can also
use ActiveX with Visual Basic for Applications (see the next chapter) and C++.
In ActiveX, objects are structured in a hierarchy. You need to understand this struc-
ture before working extensively with ActiveX. Chapter 37, on Visual Basic for
Applications (VBA), covers this hierarchical structure in more detail.
ActiveX enables you to get information about objects (called get functions) and
modify them (called put functions). The next section first reviews how you create
these two functions in AutoLISP.
Reviewing AutoLISP retrieval and modification
In this section, you look at developing a small routine, written in AutoLISP, that
mimics ActiveX properties and methods. This will help you compare how AutoLISP
works compared to ActiveX.
Cross-
Reference
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1167
1168 Part VII ✦ Programming AutoCAD
To understand Visual LISP’s ActiveX features, you need to know how AutoCAD
exposes an object’s properties in AutoLISP. The following examples work with a line
10 units long created using the following AutoLISP function:
;;; This function creates a line using the AutoLISP
;;; command function and returns nil.
(defun make-aLine ()
(command “_line” “5,5” “15,5” “”)
)
The uppercase letter (in the function name) is used for readability, but you can
type all lowercase letters if that is easier for you.
After loading it, you can use this function (that is, draw the line) by typing the fol-
lowing at the Visual LISP Console:
(make-aLine)
As explained in Chapter 35, to retrieve the last object created (the line), you use
ENTLAST as shown in the following code fragment. The next expression places the
value of the last created entity, as an entity name, to the variable LineEntity. To
try this out, type the following code in the Console.
(setq LineEntity (entlast)) ↵
Visual LISP responds with the entity name. As soon as you receive the entity name, you
can use ENTGET to retrieve the object property list of the line entity name. The following
code fragment places the property list value in the variable LinePropertyList. If you
type the following in the Console and press Enter, Visual LISP responds with the prop-
erty list:
(setq LinePropertyList (entget LineEntity)) ↵
Here is an example of a property list, formatted for readability.
((-1 . )
(0 . “LINE”)
(330 . )
(5 . “2C”)
(100 . “AcDbEntity”)
(67 . 0)
(410 . “Model”)
(8 . “0”)
(100 . “AcDbLine”)
(10 5.0 5.0 0.0)
(11 15.0 5.0 0.0)
(210 0.0 0.0 1.0))
Note
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1168
1169Chapter 36 ✦ Exploring Advanced AutoLISP Topics
As you can see, ENTGET returns an entity’s properties as a collection of lists, all of
which have a distinctive number at their first position. These group codes (also
commonly known as DXF fields because you can also find them in DXF files) were
listed in Chapter 35. For this exercise, you just need to remember that the 10 group
code is associated with the start point of a line.
Having the entity list and knowing which values to retrieve, you can use the
AutoLISP function ASSOC, which returns an associated item from a property list.
To retrieve the start point of the line, you would use the following code, which you
can type in the Console:
(setq StartofLineList (assoc 10 LinePropertyList)) ↵
The Console returns the list associated with the 10 group code, including the group
code and the coordinates:
(10 5.0 5.0 0.0)
Because the only value you require is the start point of the line object, use the func-
tion CDR to remove the first element of the list, as shown in the following code.
(setq StartofLine (cdr (assoc 10 LinePropertyList))) ↵
This code returns only the start point coordinate:
(5.0 5.0 0.0)
Now that you have reviewed how to retrieve a value from an AutoCAD object, you
can see that retrieving information from an associated list is somewhat straightfor-
ward. But how about retrieving more than one property at a time? As you can see
from the preceding example, you would have to repeat the same body of code many
times over to retrieve any information. Knowing this, you could write a simple inter-
face function, putting together all the steps just explained, to retrieve any group
code, not just the 10 group code, from an object.
An interface function is a function that hides a complex behavior to the user. The
user need provide only basic information, but the function uses the information in
several steps to obtain the desired result.
An example of an interface function is shown in the following lines:
;;; returns any group code value if it is present in the entity
;;; the required parameters are an entity name and a group
code.
(defun Get-A-Group-Code (EntityName GroupCode)
(cdr (assoc GroupCode (entget EntityName)))
)
Note
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1169
1170 Part VII ✦ Programming AutoCAD
After you create this function and load it, you can test it out in the Console as fol-
lows, using the LineEntity variable previously defined:
(Get-A-Group-Code LineEntity 10) ↵
(5.0 5.0 0.0)
As you can see, the function returns only the value of the 10 group code.
You can refine this small interface by defining a separate 10 group 10 function, such
as the following. The only required parameter is an entity name. The group code is
included in the call to Get-A-Group-Code. You could do the same for an 11 group
code, if you want.
(defun Get-Group-10-Code (anEntityName)
(Get-A-Group-Code anEntityName 10)
)
After loading, test the function, as follows:
(Get-Group-10-Code LineEntity) ↵
Visual LISP returns the start point of the line:
(5.0 5.0 0.0)
These examples summarize how you would create a simple function to get the start
point of a line using AutoLISP. What if you need to change an object’s property? You
can do this by using the functions CONS, SUBST, and ENTMOD, which Chapter 35 covers.
CONS constructs a list. Use it when you want to create new values for a group code
within an entity list, as in the following example, which you can type in the Console
window:
(setq NewStartPoint (cons 10 ‘( 0 0 0 ))) ↵
Visual LISP returns the following:
(10 0 0 0)
Using the variables NewStartPoint and LinePropertyList, you can now sub-
stitute the newly created group 10 code. You do this using the SUBST function
explained in Chapter 35. The following code substitutes the new group 10 code
represented by the variable NewStartPoint for the 10 association in
LinePropertyList in the list called LinePropertyList.
(Setq LinePropertyList
(subst NewStartPoint (assoc 10 LinePropertyList)
LinePropertyList)
)
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1170
1171Chapter 36 ✦ Exploring Advanced AutoLISP Topics
To test this out, type the preceding code in the Console window. To see the new
start point, you need to scroll all the way to the right. The list (nicely formatted)
now has a new group 10 value (the start point), shown on the third-to-last line that
follows:
((-1 . )
(0 . “LINE”)
(330 . )
(5 . “2C”)
(100 . “AcDbEntity”)
(67 . 0)
(410 . “Model”)
(8 . “0”)
(100 . “AcDbLine”)
(10 0 0 0)
(11 15.0 5.0 0.0)
(210 0.0 0.0 1.0))
To reflect the modification of this line in AutoCAD, as explained in Chapter 35, you can
now use the function ENTMOD, as follows, by typing it in the Console. This code actually
changes the start point of the line. (You can return to AutoCAD to check it out.)
(entmod LinePropertyList) ↵
As you can see from this example, getting object properties and modifying them
can be a tedious and time-consuming process. For the next example, you write an
interface function that modifies any group code contained in any object.
(defun put-group-code-value (Entityname Groupcode Value /
PropertyList)
(setq PropertyList (entget EntityName))
(setq PropertyList
(subst
(cons GroupCode Value)
(assoc GroupCode PropertyList)
PropertyList
)
)
(entmod PropertyList)
)
This function combines all the preceding steps into one function. Here’s how it
works:
✦ Line 1 defines the function with three arguments: the entity name, a group
code, and a new value for the group code. It also declares a local variable,
PropertyList, which is the property list of the object.
✦ Line 2 sets the property list equal to the ENTGET of the entity name.
✦ Line 3 starts the process of setting the same property list to the new value.
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1171
1172 Part VII ✦ Programming AutoCAD
✦ Lines 4 through 7 execute the substitution. They substitute the new group,
created by (cons GroupCode Value), for the current group value, created
with the ASSOC function, in the property list named PropertyList.
✦ Line 8 closes the SUBST function.
✦ Line 9 closes the second SETQ function.
✦ Line 10 modifies the drawing database using ENTMOD.
✦ Line 11 closes the DEFUN function.
Using the preceding function, you now have a much simpler interface for modifying
any group code. Next, you use the function Put-a-Group-Code-Value to modify the 10
group code of the line object.
After entering and loading the preceding function, you can test the function by
typing the following in Visual LISP:
(Put-Group-Code-Value LineEntity 10 ‘(5 5 0)) ↵
This function changes the start point of the line to 5,5,0.
Using the same logic to write the get functions, you can now define a separate 10
group code modifier function. You can do the same for any group code.
(defun Put-Group-10-Code (EntityName Value)
(Put-Group-Code-Value EntityName 10 Value)
)
After entering and loading this function, type the following at the Console to change
the start point of the line to 15,–5,0.
(Put-Group-10-Code LineEntity ‘( 15 -5 0 )) ↵
Activate AutoCAD to check that the line has been changed.
Using ActiveX with Visual LISP
You have just seen how to retrieve and modify object information by writing small
get and put interface functions using AutoLISP. ActiveX provides a similar way to
retrieve and modify objects but it requires some preparation. In the following sec-
tion, you read about using some ActiveX functions to create, retrieve, and modify
an object.
Retrieving and modifying object information with ActiveX
Visual LISP enables you to retrieve and modify any AutoCAD object using AutoCAD’s
ActiveX interface. That is to say, AutoCAD exposes all of its objects to ActiveX-
enabled applications. This includes Visual LISP as ActiveX objects, all of which
expose their properties, including put (modify) and get (retrieve) functions.
44 539922 Ch36.qxd 5/2/03 9:44 AM Page 1172
1173Chapter 36 ✦ Exploring Advanced AutoLISP Topics
Using Visual LISP to communicate with AutoCAD 2004 is very straightforward. You
must first load all of the ActiveX functions using the VL-LOAD-COM function within
Visual LISP. This exposes all of the ActiveX interface functions. You need to loa
Các file đính kèm theo tài liệu này:
- Programming AutoCAD.pdf