Harry's Code Class: Tips for Programmers (June 2007)25 Jun, 2007
Visual LISP Determines Paper Size Just Plotted
Developing a routine to automate this common task includes lessons about AutoCAD layout objects and reactor functions.
A basic question cropped up on the Cadalyst Hot Tip Harry-Help! Discussion Forum recently. What I found interesting was that no one seemed to have an answer, even though the writer's issue is a common one. So I went to find the answer, and this is my report. Consider yourself warned: The geek-o-meter this month is going deep into the red!
The question related to obtaining basic plot information once the Plot command completes. Specifically, the writer requested a LISP program to determine the paper size just plotted. Tracking paper use for billing or other purposes is so common throughout the CAD world that I thought surely at least one other reader would have a routine on hand to automate the process. Yet after several days, the request sat unanswered on the forum. Finally, I decided to look for the solution in AutoCAD's Help system. Sure enough, I found a method for accessing the paper size given a plot configuration or a layout object, and it seemed the mystery was well on its way to being solved. That is, if one is ready to dig into AutoCAD objects.
The first major objective is to see exactly which data can be found in both the plot configuration and the layout objects. To learn this, I used Visual LISP, of course. If you have never ventured deep into the AutoCAD objects, fire up AutoCAD and the VLIDE (Visual LISP Integrated Developer Environment), then follow along. You will see that exploring the object model can be quite revealing and rewarding.
For starters, we need to initialize the object features of Visual LISP with the expression (VL-LOAD-COM). Type this expression, including the parentheses, at the AutoCAD command prompt or in the Visual LISP console window. The figure below shows the sequence of entries (following the dollar sign prompts) and responses from the Visual LISP console.
Entries and responses from the Visual LISP Console.
After entering the code sequence shown in the figure, we can now explore these objects using VLAX-DUMP-OBJECT.
Type the expression (VLAX-DUMP-OBJECT DWG) including the parentheses in the Visual LISP Console. You will see a long list of associated properties for the drawing object. The following figure shows the start of this information dump.
Information dump of the drawing object.
The dollar sign prompt at the top of the figure precedes the expression entry. The remaining lines that begin with a semicolon contain the properties associated with the drawing object. Each property name is followed by an optional read-only (RO) marker. To the right of each equal sign is the value of each property. You will note that many of the properties are objects themselves.
The last property shown is the plot object. Obviously, that one might bring us closer to the quarry of the plot size, so let's get the plot object and dump the contents. The following is a copy of the code transcript. Underlined text is typed input.
_$ (setq PO (vla-get-plot dwg)) #<VLA-OBJECT IAcadPlot 0e60df0c> _$ (vlax-dump-object po) ; IAcadPlot: The set of methods and properties used for ; plotting layouts ; Property values: ; Application (RO) = #<VLA-OBJECT IAcadApplication 00d73d3c> ; BatchPlotProgress = 0 ; NumberOfCopies = 1 ; QuietErrorMode = 0 T
Nothing much appears here that seems useful in our quest for the plot size in this object, but before we move on, let's use VLAX-DUMP-OBJECT to examine the methods associated with the object. Adding a non-nil flag as a second parameter to the function produces a list of the methods supported by the object reference. Each method listed includes a count of the number of parameters required inside parentheses.
_$ (vlax-dump-object po T) ; IAcadPlot: The set of methods and properties used for ; plotting layouts ; Property values: ; Application (RO) = #<VLA-OBJECT IAcadApplication 00d73d3c> ; BatchPlotProgress = 0 ; NumberOfCopies = 1 ; QuietErrorMode = 0 ; Methods supported: ; DisplayPlotPreview (1) ; PlotToDevice (1) ; PlotToFile (2) ; SetLayoutsToPlot (1) ; StartBatchMode (1) T _$
Although these methods would be interesting to explore at some time in the future, this is not the prize we seek. So let us return to the drawing object to see what else might be of interest. The active layout might be a good place to check next. The following entries are a sample of what appears in the Visual LISP console. Underlined text is typed input.
_$ (setq al (vla-get-activelayout dwg)) _$ (vlax-dump-object al) ? ; CanonicalMediaName = "Letter" ? ; PaperUnits = 0 ; PlotHidden = 0 ; PlotOrigin = (0.0 0.0) ; PlotRotation = 1 ; PlotType = 1 ; PlotViewportBorders = 0 ; PlotViewportsFirst = 0 ; PlotWithLineweights = 0 ; PlotWithPlotStyles = 0 ? ; Methods supported: ? ; GetPaperSize (2) ?
Aha! Here is the GetPaperSize method ready to access. Now the question is how to get it to work. GetPaperSize has two parameters. When we look up the method in the online Help, it is described using VBA object references. It takes a little practice, but you can convert the VBA documentation into Visual LISP.
Converting VBA to Visual LISP
The first thing to consider in the VBA-to-LISP conversion is that Visual LISP does not use the object.method format to invoke an object. Instead, use VLA- with the method name and the object reference as the first parameter. Next, look at the parameters for the method you want to use. If you are sending in data, use a constant or a symbol referencing data of the proper type. If you are obtaining data from the method, as in the case of GetPaperSize, send references to the symbols to be set. In Visual LISP, you send references to a symbol using a quote mark before the symbol name.
After generating a plot, I tried the following at the Visual LISP console:
_$ (vla-getpapersize al 'x1 'x2) nil _$ x1 215.9 _$ x2 279.4 _$
Jackpot! I had just plotted a drawing in 8.5" x 11" format, and these results correctly display those paper size values in millimeters! (No matter what the units setting, the function will always return the size in millimeters.)
So let's put this all together into a simple function to determine the paper size just plotted, in inches.
(defun JustPlotted (/ AO DO AL X Y) (vl-load-com) (setq AO (vlax-get-acad-object) DO (vla-get-activedocument AO) AL (vla-get-activelayout DO) ) (vla-getpapersize AL 'X 'Y) (list (/ X 25.4) (/ Y 25.4)) )
The last step in the function is to divide the x and y values by 24.5, the number of millimeters in an inch.
Note that this function works only after you have plotted something. So the next question is, Where to catch the information? A couple of options exist, including writing a command reactor that looks for the Plot command to finish. Reactor commands might seem complicated at first; however, they are actually quite simple to program. Let's start with a simple reactor function that will run when the Plot command has just completed. The function will invoke the JustPlotted function defined above.
(defun GetPlotData (rNam Params) ;;did you just plot? (if (= (car Params) "PLOT") ;;print the paper size value (print (JustPlotted))) )
The GetPlotData function is to be called when a command has just ended, so the next step is to define that link.
(setq PlotExampleReactor (vlr-command-reactor nil '((:VLR-CommandEnded . GetPlotData))))
That is the minimum required to set up a reactor program. When creating reactors, keep in mind that too many will tend to get stacked up inside AutoCAD. Be sure your code cleans up previous reactor associations before loading or assigning new ones. Use VLR-REMOVE or VLR-REMOVE-ALL for that purpose.
Incidentally, while I was researching this solution, I came across another alternative to obtain the plot data: Use the plot log data generated by AutoCAD whenever a plot is run. This is an optional feature in AutoCAD, normally enabled, where a comma-delimited file is built each time the Plot command completes. By default, the file is appended each time a plot completes. However, you also can opt to have a log file created using the drawing name. Log files created using the comma-delimited format can be imported directly into most spreadsheet programs. Or you can opt to write your own programs that read and process the information in the log file.
This was a great question and deserved this in-depth exploration. Do you have a question about AutoCAD programming or a suggestion for Harry's Code Class? Please e-mail HarrySuggestionBox@cadalyst.com. How about a cool tip or two to share with Harry and the rest of the world? Send them to firstname.lastname@example.org. Harry is eager and waiting — and you could win a Cadalyst t-shirt, $100, or even a trip to Autodesk University 2007 in Las Vegas! Also be sure to visit Cadalyst's Hot Tip Harry-Requests Discussion Forum to post your requests for AutoCAD functions.
Until next time, keep on programmin'!
Cadalyst Discussion Forum Original Thread: "Plotting"
Check out the post on the Hot Tip Harry-Help! board that was behind this month's newsletter topic. Read more >>
Using Automation in Visual LISP
In this Autodesk training article, Bill Kramer reviews how to use -- from within Visual LISP -- some of the ActiveX tools built into AutoCAD. Read more >>
Reactors -- Making Aware Programs
Bill Kramer's Cadalyst article discusses how to use Visual LISP reactors to make an application aware of program events. Read more >>
"Actual Paper Size?" Discussion
From an Autodesk discussion group come a few LISP options related to obtaining paper size data. Read more >>