Introduction

By Scott Hull


This is the second in a series of AutoLISP tutorial columns. The purpose of this column is to give an introduction to dialogue boxes. A sample application that draws tapped and/or clearance holes for small machine screws is used as the example.

We will start by loading and running the sample program that draws 2D holes. The program consists of two parts, both of which must be saved into an AutoCAD support directory in order to use them. An AutoLISP dialogue box typically requires two files, the lisp code and the DCL (Dialogue Control Language) file. The DCL file defines the layout of the dialogue box. The lisp file contains the code to control the components in the dialogue box.

The Visual LISP Developer's Guide included with AutoCAD 2000 covers all of the various components for dialogue boxes. The examples that I use in this tutorial is purposely limited but should provide enough information so that you can view and understand lisp code that uses DCL files.


DCL - Short History

DCL (Dialogue Control Language) was implemented by Autodesk during a period of time when DOS was still the most common operating system being used on personal computers. But there were many new operating systems that were gaining popularity. Autodesk had ported AutoCAD to run on many of these, including DOS, Irix, MAC, OS2, Solaris, Xenix, and later to Windows.

Many of the new operating systems had graphical interfaces and Autodesk started to use the native dialogue boxes to enhance the drawing editor. At that same time there was a large 3rd party developer community using AutoLisp and they were asking for programmable dialogue box capability beyond the limited slide icon dialogue box that was available in AutoCAD.

Someone at Autodesk had an inspiration to create a generic dialogue box language for use with AutoLisp. Each of the many operating systems would have it's own translation layer that would interpret the DCL file and display the dialogue box using the components in the native operating system. The most popular operating system at the time, DOS, did not have a graphical operating system so Autodesk wrote Proteus in order make the DOS version of AutoCAD act like a graphical operating system. It was then possible to write AutoCAD applications for all of the available operating systems using the same program code. In practice, differences in the graphical operating system often required the programmer to do some tweaking to the code. Overall, the DCL and Proteous additions to AutoCAD turned out to be an ingenious and very useful addition to AutoCAD.

When Windows finally became powerful enough to run AutoCAD, Autodesk dropped support for the other operating systems. But the DCL language was retained and is still the easiest way to create dialogue boxes in AutoCAD applications.


Part One - SCRHOLE


Getting Started - Lisp and DCL files

View the scrhole.lsp program. This is the lisp code for controlling the dialogue box and drawing the holes. Save it to the AutoCAD support directory.

View the scrhole.dcl DCL file. This is the DCL code for defining the dialogue box layout. Save it to the AutoCAD support directory.

You can use the AutoCAD APPLOAD command or lisp load function to load the program inside AutoCAD:

Command: (load "scrhole")

Run the scrhole program:

Command: scrhole

A dialogue box similar to the following figure will be displayed:

Screw Hole Dialogue Box

Size
This pop-up list sets the size of the hole. A pop-up list is useful for displaying lists of arbitrary length without using much space in the dialogue box. The similar usage list box uses more space but is sometimes quicker for the user.
Clearance Hole
This radio button sets the type to be a clearance hole. A group of radio buttons is used to set mutually exclusive options. A toggle is another type of tile/widget/component that can be used to set non-exclusive types of options.
Tapped Hole
This radio button sets the type to be a tapped hole.

Pick OK to continue.

Center point: Pick point.

The program will draw a clearance hole for a #6 screw. You can try different sizes and the Tapped Hole option to see how the components are used. Next, I will analyze the portion of the lisp code that draws the hole.


Part Two - SCRHOLE


The scrhole.lsp file contains the lisp source code. The main function is C:SCRHOLE and it contains several nested functions. I will not cover all of these in detail as similar explanations can be found in my previous column, SPIRAL. In this part, I will briefly cover just the @HOLE-DRAW function which uses data and draws a clearance or tapped hole. The rest of the lisp code that is actually associated with the dialogue box will be analyzed in part four after covering the DCL code in part three.

 (defun @HOLE-DRAW (/ #PT0)
  (cond
   ((= #TYPE "clr")
    (setq #DIA0 #CL_DIA))
   ((= #TYPE "tap")
    (setq #DIA0 #TAP_DIA #DIA1 #NOM_SIZE)))
  (initget 1)
  (setq #PT0 (getpoint "\nCenter point: "))
  (setq #OSMODE (getvar "osmode"))
  (setvar "osmode" 0)
  (setvar "cmdecho" 0)
  (command "_.circle" #PT0 "_d" #DIA0)
  (if (= #TYPE "tap")
   (progn
    (setq #CELTYPE (getvar "celtype"))
    (setvar "celtype" "hidden")
    (command "_.circle" #PT0 "_d" #DIA1)
    (setvar "celtype" #CELTYPE)))
  (setvar "osmode" #OSMODE))

The @HOLE-DRAW function uses data stored in several variables. Most of the user supplied data will be acquired through the dialogue box interface rather than through the command: prompt.

>>(cond
>>>((= #TYPE "clr")
>>>>(setq #DIA0 #CL_DIA))
>>>((= #TYPE "tap")
>>>>(setq #DIA0 #TAP_DIA #DIA1 #NOM_SIZE)))

The #TYPE variable will be set by dialogue box interaction which I will cover in Part Five. It can have a value of "clr" (CLeaRance) or "tap" (TAPped). When drawing a clearance hole, it uses the #CL_DIA (CLearance DIAmeter) only. When drawing a tapped hole, it uses the #TAP_DIA (TAP DIAmeter) and #NOM_SIZE (NOMinal SIZE) variables.

>>(initget 1)
>>(setq #PT0 (getpoint "\nCenter point: "))

As stated earlier, most of the variables are acquired through the dialogue box interface. The #PT0 variable is the exception since it requires that the user pick a point. The dialogue box cannot be active when the getpoint request is made.

>>(setvar "osmode" 0)
>>(setvar "cmdecho" 0)
>>(command "_.circle" #PT0 "_d" #DIA0)

A couple of system variables are set to insure that the program will behave correctly and then the first circle is drawn.

>>(if (= #TYPE "tap")
>>>(progn
>>>>(setq #CELTYPE (getvar "celtype"))
>>>>(setvar "celtype" "hidden")
>>>>(command "_.circle" #PT0 "_d" #DIA1)
>>>>(setvar "celtype" #CELTYPE)))
>>(setvar "osmode" #OSMODE))

If the type of hole is tapped, another circle will be drawn, but this one is hidden to represent the threads. Finally, to be polite to the user, the program returns system variables to their original settings.

In the next section, I will analyze the DCL code that is used to define the dialogue box.


Part Three - SCRHOLE


The scrhole.dcl file contains the dialogue box definition. It controls which components are included and how they are laid out. It is also used to assign a "key" or name to each component or group so that they can be accessed with special lisp function calls.

Analyze DCL Code

scrhole : dialog {
  label = "Screw Holes";
  : row {
    : popup_list {
      label = "&Size:";
      key = "holesize";
      width = 26;
    }
    : radio_column {
      key = "type";
      : radio_button {
        key = "clr";
        label = "&Clearance hole";
      }
      : radio_button {
        key = "tap";
        label = "&Tapped hole";
      }
    }
  }
  spacer;
  ok_cancel;
}

Portions of the column showing DCL code will be in blue. As you can see, DCL allows you to define a dialogue box using fairly simple code.


Define Dialogue Box

scrhole : dialog {
...

The scrhole dialogue box is defined at the start and then followed with a curly brace "{". Each open "{" must have a corresponding closing "}" similar to the opening and closing parenthesis used in lisp code. Some single line tiles or subassemblies do not use curly braces. I indent two spaces for each new "{" in order to make the code more readable.


...
  label = "Screw Holes";
...

The second line is a "label" for the dialogue box. Most tiles can utilize the "label" attribute. Depending on the type of tile, other attributes are allowed. A "dialog" tile normally uses only the "label" attribute. Convention is to make the first letter of each word in the "label" phrase a capitol. This label text will appear at the top of the dialogue box.


...
  : row {
...

By default, tiles are arranged in a column. You can force the tiles to be laid out in a row (or column).


...
    : popup_list {
      key = "holesize";
      label = "&Size:";
      width = 26;
    }
...

A "popup_list" is used to present the user with a selection of hole sizes. The "key" is the most important attribute of a tile. The "key" specifies the string that we will use in our lisp code in order to access that particular tile. In a later section of this column, I will show the lisp code that is needed to write to and read from this tile. The label is "&Size:". The "&" character specifies that the next character in the string is used as a "mnemonic" character. The "S" in the label will be underlined to indicate that it is a mnemonic. When inside the dialogue box, you can press the "S" keyboard key to jump to that tile. DCL also has a "mnemonic" attribute that performs the same function. The optional "width" attribute is to specify the width of the tile, including the label. Otherwise the width is what ever fits in the available area. As with most of the attributes that control size and spacing, it is best to try the dialogue box first without it. Then, if it does not look right, specify a width.


...
    : radio_column {
      key = "type";
      : radio_button {
        key = "clr";
        label = "&Clearance hole";
      }
      : radio_button {
        key = "tap";
        label = "&Tapped hole";
      }
    }
...

Radio buttons are mutually exclusive. You can have a cluster of as many as needed but only one can be on at any given time. I used the "radio_column" tile to arrange them vertically. I used "type" as a key to access the cluster. The two buttons set the hole type to either a clearance hole or a tapped hole.


...
  spacer;
  ok_cancel;
}

The dialogue box definition finishes with the predefined "ok_cancel;" cluster. Similar to defining callable functions in AutoLisp, you can define your own complex tiles and use them in other dialogue boxes. The "ok_cancel;" tile is in the base.dcl file that comes with AutoCAD and is always loaded. It contains an "OK" and a "Cancel" button. The "key" for the "OK" button is "accept" and the "key" for the "Cancel" button is "cancel". I used a "spacer;" tile between the main part of the dialogue box and the "ok_cancel;" in order to improve the appearance by spacing them out.

In Part Four, I will show how lisp is used to access the dialogue box.


Part Four - SCRHOLE


...
 (if (< (setq #dcl-id (load_dialog "SCRHOLE")) 0)
  (quit))
 (if (not (new_dialog "scrhole" #dcl-id))
  (quit))
 (start_list "holesize")
 (foreach #x #list (add_list (car #x)))
 (end_list)
 (@size "10")
 (set_tile "holesize" "10")
 (setq #type "clr")
 (set_tile "type" #type)
 (action_tile "accept" "(done_dialog 1)")
 (action_tile "holesize" "(@SIZE $value)")
 (action_tile "type" "(setq #TYPE $value)")
 (setq #go (start_dialog))
 (if (= #go 1) (@hole-draw))
...

The above portion of the lisp code handles all communication with the dialogue box.

>(if (< (setq #dcl-id (load_dialog "SCRHOLE")) 0)
>>(quit))

This line of code executes the "load-dialogue" function and saves the returned value in the #DCL-ID variable. If the value is less than 0, it indicates that the DCL file did not load properly and then quits. If a positive value is returned, it indicates that the DCL file did load and the program continues.

>(if (not (new_dialog "scrhole" #DCL-ID))
>>(quit))

This next line looks in the loaded DCL file indicated by #DCL-ID for a dialogue box named "scrhole". For simple dialogue boxes such as this, I usually make the dialogue name the same as the DCL file name. But, in the same manner that you can define multiple functions inside of a single lisp file, you can define multiple dialogues inside of a single DCL file. Either the new dialogue is successfully started or I force the program to quit.

Once the "new_dialog" function is executed, that dialogue box is active and ready to accept or supply data to the lisp program.

>(start_list "holesize")
>(foreach #X #LIST (add_list (car #X)))
>(end_list)

The "start_list" function is used to feed a list of data to the pop-up called "holesize". I included a list of threaded hole data stored in a variable called #LIST in the lisp file. A portion of the list is shown below:

...
 (setq #LIST (list
'(".073-64 UNC - #1" 0.073 0.0595 0.081)
'(".073-72 UNF - #1" 0.073 0.0595 0.081)
'(".086-56 UNC - #2" 0.086 0.07 0.096)
'(".086-64 UNF - #2" 0.086 0.07 0.096)
...
Each sub-list (#X) contains four items, the size string, the nominal size of the hole, the tap drill diameter, and the clearance hole diameter. The size string (car #X) in each sub-list is added to the pop-up list in the dialogue box that the user will select from.

>(@SIZE "10")
The SCRHOLE lisp program contains another imbedded function definition called @SIZE. The @SIZE function takes an integer in string format and uses that number to retrieve the appropriate screw hole size. The reason that a string is used rather than a simple integer, is because the value returned by a pop-up list selection made by the user is a string. That string represents an integer indicating which item in the pop-up list was selected. The @SIZE function sets the variables #NOM_SIZE, #TAP_DIA, and #CL_DIA needed to draw the specified hole. The initial setting of "10" indicates the ".138-32 UNC - #6" size. Remember that the value returned for the first item in a pop-up list is "0", not "1".

>(set_tile "holesize" "10")
The "set_tile" function is used to set the value of a tile in the active dialogue box. I set the "holesize" pop-up list to the same value as the initial data for the ".138-32 UNC - #6" size. The value that is used to set the tile must be in the same form as that which the tile returns when it is selected by the user. In this case it is the string "10" that represents an integer indicating which item in the pop-up list to display.

>(setq #TYPE "clr")
>(set_tile "type" #TYPE)

I want the initial type of hole to be a clearance hole. I set both the #TYPE variable and "type" tile to reflect this.

So far, I have indicated which dialogue box to load, filled the pop-up list and indicated what values I want to display initially. I still have to specify what actions are taken when any of the buttons or other tiles is pressed.

>(action_tile "accept" "(done_dialog 1)")
>(action_tile "holesize" "(@SIZE $value)")
>(action_tile "type" "(setq #TYPE $value)")

The "action_tile" function is used to associate an action with a button or tile. The "accept" or "OK" button calls the "done_dialog" function and instructs it to return a value of 1. Notice that the actions to be taken must have quotes at the start and end of the sequence. I still haven't figured out why the programmers that created DCL did it this way. The next line causes any selection in the "holesize" pop-up list to call the @SIZE function. The "$value" passed to the "holesize" function is the integer string that indicates which item in the list was selected. "$value" is available for any tile and it's form depends on the type of tile selected. The third line is the action for the "type" cluster of radio buttons. It is also possible to read individual radio buttons by having an "action_tile" for each one. But it usually more expedient to have a single "action_tile" apply to the cluster of radio buttons.

>(setq #GO (start_dialog))
After all of the initial "set_tile" and "action_tile" calls are made, I can start the dialog box with the "start_dialog" function. I set the variable #GO to whatever value is returned when the dialogue box is completed or canceled.

>(if (= #GO 1) (@HOLE-DRAW))
Once the dialogue box is completed, I check the value of #GO. If it is anything other than 1, I assume that the "Cancel" button was pressed. A value of 1 indicates that the "OK" button was pressed and I proceed to draw the screw hole.

In Part Five, I will cover some additional dialogue box information and possible changes/enhancements that you can make to the SCRHOLE program.


Part Five - SCRHOLE


AutoCAD has a built in alert dialogue box that is useful for presenting message text information to the user. It is usually better than trying to present large amounts of information at the Command: line. Since the DCL code and the function to call it are built into AutoCAD, it is easy to use. You can type the following example at the AutoCAD Command: prompt to see it in action:

Command: (alert "Line one.\nLine two.")

A dialogue box similar to the following figure will be displayed:

Alert Dialogue Box


Dialogue boxes can be nested. In other words, you can call another dialogue while one is still active. So rather than simply quitting the program when you encounter a failed dialogue box, you can "alert" the user.

The original code I used is shown below in red:

...
 (if (< (setq #dcl-id (load_dialog "SCRHOLE")) 0)
  (quit))
...

You can modify the code as shown below to display the alert message:

...
 (if (< (setq #dcl-id (load_dialog "SCRHOLE")) 0)
  (progn
   (alert "DCL file failed to load!")
   (quit)))
...

The "progn" function is used to group the "alert" and "quit" functions together so that they both execute when "if" sees that the #DCL-ID is less than 0 indicating that no DCL identifier was obtained.


The other "quit" function call that is used on failure to start a new dialogue boxed can also be supplemented with an alert message:

...
 (if (not (new_dialog "scrhole" #DCL-ID)) (quit))
...

You can modify the code as shown below to display the alert message:

...
 (if (not (new_dialog "scrhole" #DCL-ID))
  (progn
   (alert 
    "scrhole dialogue box is not available!")
   (quit)))
...

We can change the "look and feel" of the dialogue box without changing the lisp code. View the scrhole1.dcl DCL file. This is the scrhole DCL code with some modifications. Save it to the AutoCAD support directory and rename it to scrhole.dcl. Run the scrhole program again.

A dialogue box similar to the following figure will be displayed:

Screw Hole Dialogue Box

Notice that the "popup_list" is now a "list_box":

...
    : popup_list {
      key = "holesize";
      label = "&Size:";
      width = 26;
    }
...

The modified DCL file replaces it with the following:

...
    : list_box {
      key = "holesize";
      label = "&Size:";
      width = 26;
      height = 5;
    }
...

I added a new attribute to limit the height of the list box to 5 units. When space is not limited, the "list_box" is often preferable to the "popup_list" since the user can often make another selection without having to scroll through the list.

Also notice that the cluster of two radio buttons is now inside a box with a label of "Type". Notice that the "radio_column" is now a "boxed_radio_column":

...
    : radio_column {
      key = "type";
...

The modified DCL file replaces it with the following:

...
      : boxed_radio_column {
        key = "type";
        label = "&Type";
...

The "boxed_radio_column" can also use a label.

I added another button but did not add any lisp code to utilize it. The new button is a "toggle" to indicate that the hole will be drawn with a hidden linetype.

Since I used a "row" tile in the original DCL file, I also had to add a "column" tile in order to make the new "Hidden" toggle appear in the desired location in the dialogue box.

...
    : radio_column {
      key = "type";
...

Your assignment is to make this new toggle work. You will have to use the "action_tile" lisp function to set a variable. The @HOLE-DRAW function will have to check the new variable and see if it should set the "hidden" linetype before drawing the clearance and nominal size circles.


I hope that this column was helpful in understanding AutoLisp based dialogue boxes. You should use it as a basis for writing or simply understanding more involved lisp applications that use DCL files.