Visual LISP: Using AutoCAD Automation In this article, you will learn how to use—from within Visual LISP®—some of the ActiveX tools built into AutoCAD® software. In my experience, many Visual LISP programmers forget about the rich library of automation tools available in the form of AutoCAD objects when they’re creating applications. So when a reader asked me how to change the parameters of existing AutoCAD blocks and how to add and remove objects from a block definition from inside Visual LISP, I decided to write this article.
| Note: I have also posted the functions associated with these tasks on the “Scrolls and Potions” portion of the Wizard's Lab.) |
From a programming point of view, you know what automation is all about: making available a set of objects with specific methods and properties via an application. Of course, that word has a different meaning when you’re talking about the ActiveX-enabled automation tools in AutoCAD. In that context, automation means driving AutoCAD in a different way. Because Visual LISP is already running inside AutoCAD and has excellent access to the system, most VLISP programmers typically just overlook these AutoCAD automation tools. But they shouldn’t. You can use these tools in your applications for tasks that Visual LISP can’t handle—such as adding or removing an entity from a block definition. Step 1: Load COMTo use the VLISP automation tools in your applications, you must load the Component Objects Manager (COM) using the expression (VL-LOAD-COM). I suggest you put this expression outside all function definitions so that it is evaluated when your program is first loaded into Visual LISP. If the COM is already loaded, the load request is ignored and the function returns to the calling place. Once you load COM, you’ll have access to a host of new Visual LISP functions that all start with the prefix VLA.
To list these new functions, use the Apropos feature inside the VLIDE (Visual LISP Integrated Development Environment). Here’s how: | Figure 1: Select the Apropos feature in the VLIDE. |
| 1. Click the Apropos icon (see Figure 1) on the VLISP View toolbar, which opens the Apropos Options dialog box.
2. You must indicate which functions list you want to see, so enter vla in the text box.
3. Select the Match by prefix check box.
4. Click OK.
Figure 2 shows what the Apropos Options dialog box should look like just before you click OK to display the VLA list. | Figure 2: Apropos Options dialog box. |
After you click OK, the Apropos Results dialog box opens (see Figure 3) showing VLA functions. Note the message “Too many symbols starting with ‘vla’” at the bottom of this dialog box. It’s there because so many VLA functions are available they can’t all be listed. | | Figure 3: Apropos results for VLA prefix. |
| Don’t let this embarrassment of riches discourage you. You’ve already worked with many of these functions, but you know them under other names. For example, you’ve probably already handled all the vlaAdd functions using ENTMAKE (and in some cases COMMAND). The only thing different about using the vlaAdd functions, rather than the commands you already know, is that you must specify the block to which the Entity object is being added. We’ll get back to this in a moment of course.
The real question is, How do I learn what these Apropos functions mean? Although Visual LISP Help only minimally covers VLA functions, it does suggest where you can learn a lot more—the Help files for Visual Basic for Applications (VBA) inside AutoCAD. Since most of these functions are automation functions and typically used by VB programmers, it is only logical that information about using them shows up in VBA Help files. What you must do, as a Visual LISP programmer, is learn how to read VBA Help and then apply what you learn to Visual LISP. The good news is that with a little practice, you’ll become adept at that task very quickly. Step 2: Attach to the AutoCAD Object of InterestAs with any automation system, first you have to gain access to the object you want to manipulate. Since AutoCAD software is already up and running, using the function (VLAX-GET-ACAD-OBJECT) opens a direct channel to the AutoCAD application object. (And from there the entire AutoCAD system, and all its automation tools, opens up for you to use in Visual LISP.) The (VLAX-GET-ACAD-OBJECT) function returns an object reference that you can save and retrieve later using a Visual LISP symbol or variable name. Here’s the expression:
(setq acadObj (VLAX-GET-ACAD-OBJECT))
In this instance, the object reference is saved as the symbol acadObj.
The AutoCAD application object has many interesting properties (objects) attached to it. For example, from this object you can drill into the plotter drivers or the display configuration. For your application, you want to access all the open AutoCAD drawings (the documents collection).
Obtaining the documents collection, or any other property of the AutoCAD application object, is just a matter of knowing the right name to use. Start with VLA-GET- and then add the name of the VBA property. For example, the following expression obtains the object that is the collection of all open AutoCAD documents.
(setq docsObj (VLA-GET-DOCUMENTS acadObj))
The symbol docsObj references an object that is a collection. A collection is a group of objects of the same type. In this case, each object in the collection is a document.
To obtain a particular Document object from the collection of documents, use the ITEM method. ITEM is available for all collection objects. In Visual LISP that translates to VLA-ITEM. Since VBA Help shows the item syntax as object.ITEM(index), VLA-ITEM requires two parameters: the collection and an index value. The index value may be an integer, or it may be a string with which you can reference individual objects in the collection. Almost all collections have a names index unless having one makes no sense for the specific collection. For example, selection sets have no names index. But the open documents collection does. You can reference each document in the collection using the drawing name. The following expression accesses the Document object of a currently open drawing.
(setq curDocObj (VLA-ITEM docsObj (getvar "DWGNAME")))
You will find even more objects at the document level. All the tables (Blocks, Layers, Styles, and so forth) are here as is access to a set of methods for user I/O and system control related to the open drawing. Of course, you can access many document-level objects using more direct approaches in Visual LISP. Even so, accessing some document-level objects via this method gives you access to functionality that Visual LISP per se does not offer. For example let’s look at the Block table.
This table automatically contains two entries—Model Space block and Paper Space block—for each drawing, whether or not you used blocks in the drawing. The table also contains any blocks that you add to a drawing using the BLOCK command or by inserting a drawing with INSERT.
When you’re using the Block Table object to create a Block object in your application, you simply append Entity objects to the Block object. But you do not use the Visual LISP BLOCK command or ENTMAKE command to define a BLOCK entity ending with ENDBLK to do so. (And if you need to build a couple of blocks at the same time, you can, using Block object references.)
Inside AutoCAD, the block definitions in the block table are maintained in the same manner as model space and paper space definitions. And you know you can add and delete objects from model space and paper space (layout spaces) any time you wish. This, then, becomes the key to solving the puzzle of how to create a Visual LISP function that adds or removes individual Entity objects from a block definition. Because the object-based ActiveX interface tools allow a user to update the block definition at any time, you can write a Visual LISP program to perform the required tasks.
So far, the expression we’ve been working on just accesses the Document object (a currently open AutoCAD drawing). But you want to get your hands on the Block object so you can add objects to it. So given the Drawing object reference, you can now access the Block Table object with the following expression.
(setq bTblObj (vla-getblocks curDocObj))
The Block Table object is really just a collection of Block Definition objects. By accessing the Block Table object (bTblObj), you can then retrieve a block using that block’s name and VLA-ITEM as you did when you retrieved the Document object from the document collection. For example, the following expression returns the reference for a Block object named “TEST.”
(setq testBlockObj (vla-item bTblObj "TEST"))
You now have access to the Block Definition object and can append new items to it—but not by using ENTMAKE or COMMAND. You add new objects by appending them to the Block Definition object using the tools provided for that sort of activity as VLA functions Step 3: Manipulate the AutoCAD Object of InterestYou manipulate AutoCAD objects by changing the values of their properties and by running the associated methods for the given objects. The methods available for an object are functions in Visual LISP. This is where all the VLA- and VLAX-type functions originate when COM is loaded. The set of methods in all the objects is converted to Visual LISP functions with the VLA prefix.
As I indicated earlier, the real key to exploiting AutoCAD objects in Visual LISP is to learn how to translate the VBA Help information into the VLA functions that accomplish the desired tasks. The object hierarchy may be seem odd at first if you have been working with AutoLISP® rather than Visual LISP. The tables and entity lists are stored differently in terms of the object system.
To learn more about the VLA functions, go to the Help system for the VBAIDE or open the VBAIDE Object Browser dialog box.
| Note: When I am navigating unfamiliar object territory, I start the VBAIDE window in AutoCAD along with the VLIDE. |
To display this browser:
1. Enter VBAIDE at the AutoCAD command prompt, which starts Microsoft Visual Basic.
2. When the Visual Basic window is visible (and has focus), press the F2 function key and the Object Browser dialog box opens (see Figure 4).
Alternatively, you can open the Object Browser by selecting View > Object Browser from the VBAIDE main menu or by clicking the icon of a box with little boxes popping out of it (if it’s visible) on the Standard VBAIDE toolbar. | Figure 4: Object Browser dialog box in the VBAIDE. |
| If you look at the bottom of the Object Browser dialog box, you’ll see a description of the method (indicated by the flying eraser icon in the Members of 'AcadBlock' list) for adding text. That function, named AddText, returns an object of the type AcadText. It is a member of the AcadBlock object and has the basic insertion parameters of the point and height.
Unlike an ENTMAKE where you supply all the parameters of the entity list for an object like TEXT, when you use AddText you need only define the insertion point and the height value. The remaining details—layer name, text style, oblique angle, and so forth—are obtained from the current system default settings. If you need to adjust these items, you will have to assign the property directly once the object reference for the TEXT object is in hand.
Also keep in mind that an Add function is available for every Entity Object type and that you have to use it. A singular Entity Add function to which you supply a reference to an existing object and say “add this to the block” is not available. To add an object to an existing block you must be quite specific.
The example program I wrote to accompany this article demonstrates exactly what I am talking about. In that example, I included support for the LINE, ARC, and CIRCLE objects. Now, you are going to add support for TEXT. Locate the start of the COND expression in the sample program to see the tests for the LINE, ARC, and CIRCLE object types. Just before the comment line that says "Add more objects!" you are going to insert a new line and provide for the TEXT object.
Add the following and you are on your way:
((= eType "TEXT") ;Support for text entity object
The function you will be using, and the parameters you’ll need, are taken directly from the VBAIDE Object Browser dialog box. All you must do is add (VLA-) to the front, supply the object reference as one of the parameters, and make sure you are sending the correct data:
(VLA-AddText ;name of the function acadObj ;reference to block object <<the value point>> <<the value height>> )
Your application must convert the point and height values from the data types you typically use in Visual LISP to the data types the ActiveX automation routines require. Visual LISP provides a set of very useful functions to accomplish the conversions. You will use two of them, VLAX-3D-POINT and VLAX-MAKE-VARIANT.
The VLAX-3D-POINT function accepts a list representing a point in Visual LISP and returns a “safe array” variant data type suitable for passing to all the VLA functions. Normally, this is done without a temporary variable, so you can replace the <<THE value point>> from the example code above with the expression:
(VLAX-3D-POINT <<insert point text of>>)
Look at the example program. Note that a base point is supplied (BP). The base point is the insert point or zero point for the block. You must subtract the base point from the text insertion point in the entity data list (EN in the code) before the conversion function VLAX-3D-POINT. If you don’t convert the point, the text will be added to the block definition using the zero point of model space. That makes it difficult for someone using your program, who will most likely be using an insertion occurrence and not the original block definition.
In Visual LISP, subtracting one point from another is easy. It just looks funny until you get used to seeing it:
(mapcar '- (cdr (assoc 10 EN)) BP)
The above expression subtracts BP from the text base point in the entity data list. Thus, the expression for the text insertion point supplied to the VLA-ADDTEXT function should be as follows.
(VLAX-3D-POINT (mapcar '- (cdr (assoc 10 EN)) BP))
Now supply the text height. Take the text height value from the entity data list (found using group code 40), and simply pass that value to the (VLAX-MAKE-VARIANT) function. Change <<THE value height>> to read:
(VLAX-MAKE-VARIANT (cdr (assoc 40 EN)))
Now add the closing parentheses in the right locations and your version of the AppendBlock function will now support Text objects. SummaryMany Visual LISP purists do not want to use the ActiveX automation approach to controlling various AutoCAD features. At the same time, those with ActiveX experience in other programming languages tend to think that the approach taken in Visual LISP is strange. But a true wizard of the technology knows that there are many ways to accomplish a desired task, that you must fully exploit all the provided tools to achieve the best forms of magic.
Although the object system may appear fuzzy and odd, and more importantly, written for another programming language, Visual LISP programmers shouldn’t avoid it. After all, there are some things you can’t accomplish with entity lists and lists alone! |