AfraLISP - Learn AutoLISP for AutoCAD productivity

Dialog Boxes in Action

by Kenny Ramage

So here we are. Dialog boxes (or Dialogue as we British say), the bane of all AutoLispers! I must say they are quite a pain in the backside. This is when Visual Basic comes into its own. Creating dialog boxes using VB is a pure pleasure. (It's the rest that's a pain). Anyway, I digress, so stuff the torpedoes and full steam ahead.

In this tutorial we are going to try to write a program that draws structural steel beams. The user will select the size of beam from a list in a dialog box. The data for the beam will then be retrieved from an external data file. We will then draw the beam without touching our mouse or keyboard. (Pure Magic!)

This is a complete working example of parametric programming. First, lets start by writing the data file.

Create a file named BEAM.DAT containing the following :

You don't want to type? O.K. I'll be kind to you. Click here for the source code - (3 Kb)

**H X B X T1 X T2 X R1

The first line (*100x55x8) is the name of the beam. (100 deep x 50 wide x 8 kg/m beam.) The second line contains the physical dimensions that we need to draw the beam. (100.0,55.0,4.1,5.7,7.0) (Height, width, web thickness, flange thickness and root radius.)

Next we need to write our DCL (Dialog Control Language) file. Name this file BEAM.DCL. The coding is below :

beam : dialog {				//dialog name
       label = "Beams.";		//dialog label
     : list_box {			//list box
        label = "&Choose Section :";	//list box label
        key = "selections";		//key to list
        height = 12;			//height
        allow_accept = true ;		//allow double clicking
     ok_cancel ;			//OK and Cancel Buttons
     :text_part {			//Text Label
      label = "Designed and Created";
     :text_part {			//Text Label
      label = "by Kenny Ramage";

As you can see, the first line is simply the name of the dialogue box. Remember, this is the name that AutoLISP knows the box by. The second line is the label attribute. This is the wording that you see at the top of the box when it is displayed. The third line displays a list box. This has it's own attributes :

  • A label attribute - Wording that is displayed above the list box.
  • A key attribute - This is the key that Autolisp uses to refer to the list.
  • Height - Simply the height of the box. If your list exceeds the height of the list box vertical scroll bars are added.
  • Allow_accept - Instead of selecting an item from the list and then the OK button, this allows you to select by double-clicking the list item.

The next section displays pre-constructed OK and Cancel buttons. (Refer to the AutoCAD Customisation Manual for more examples of these.) The last section is were you put your name, E-Mail address, Tel. Number, Thank You notes to me, etc.

Take note of were the { and } occur. DCL is very similar to AutoLISP in that what you open, you must close.

O.K Time for a rest and a cup of tea. (brantea!)

What! Back already? O.K. Now let us try and tie this lot together. Time for the AutoLISP coding. Ready, here we go :

(defun DTR (a)					;convert to radians
  (* pi (/ a 180.0))
(defun C:BEAM (/)				;function defined
;;;*Initialise and Setup Dialogue Box
  (setq OLDECHO (getvar "CMDECHO")		;store system variables
        OLDBLIP (getvar "BLIPMODE")
	OLDSNAP (getvar "OSMODE")
  (setvar "CMDECHO" 0)
  (setvar "BLIPMODE" 0)
  (setq	NAMES '("100x55x8" "120x64x10" "140x73x13" "160x82x16"
		"180x91x19" "200x100x22" "203x133x25" "203x133x30"
		"254x146x31" "254x146x37" "254x146x43");list of names
  (setq dialogshow T)				;set flag
  (setq dcl_id (load_dialog "beam.dcl"))	;initialise dialog box
  (if (not 					;check dcl exists
        (new_dialog "beam" dcl_id)		;load into memory
    (setq dialogshow nil)			;if not exit
(if dialogshow					;if dialog
(progn  					;continue with programme
(start_list "selections")			;start list box
  (mapcar 'add_list NAMES)			;add names from list
  (end_list)					;end list box
    "cancel"					;if cancel selected
    "(done_dialog) (setq userclick1 nil)"	;close dialog, set flag
  );action tile
    "accept"					;if OK or double click
      "(progn (setq SIZA (atof (get_tile \"selections\")))" ;get size of beam
      "(done_dialog) (setq userclick1 T))"	;close dialog and set flag
  );action tile
  (start_dialog)				;display dialog
  (unload_dialog dcl_id)			;unload dialog
  (if userclick1				;if OK or double click
    (progn					;continue with programme
      (setq SIZA (fix SIZA))			;convert index to integer
      (setq SIZA (nth SIZA NAMES))		;retrieve name from list
;;;*Retrieve Data from External Data File
;;;*This section is covered in the External Data Tutor
      (setq dlist nil
	    size  (strcat "*" SIZA)
	    file  (findfile "beam.dat")
	    fp    (open file "r")
	    item  (read-line fp)
  (while item
    (if	(= item size)
      (setq data (read-line fp)
	    item nil
      (setq item (read-line fp))
;;;*Format List
  (if data
      (setq maxs  (strlen data)
	    count 1
	    chrct 1
      (while (< count maxs)
	(if (/= "," (substr data count 1))
	  (setq chrct (1+ chrct))
	  (setq	numb  (atof (substr data (1+ (- count chrct)) chrct))
		dlist (append dlist (list numb))
		chrct 1
	(setq count (1+ count))
      (setq numb  (atof (substr data (1+ (- count chrct))))
	    dlist (append dlist (list numb))
  );if data
  (close fp)
;;;*This routine draws the beam.
;;;*This section is covered in the Calculating Points Tutor
     (mapcar 'set '(H B T1 T2 R1) dlist)
      (setq OLDSNAP (getvar "OSMODE"))
      (setq IP (getpoint "\nInsertion Point: "))
      (setvar "OSMODE" 0)
      (setq P1	(polar IP 0 (/ T1 2))
	    P2	(polar P1 (DTR 90.0) (/ (- H (+ T2 T2 R1 R1)) 2))
	    P3	(polar P2 (DTR 90.0) R1)
	    P4	(polar P3 0 R1)
	    P5	(polar P4 0 (/ (- B (+ T1 R1 R1)) 2))
	    P6	(polar P5 (DTR 90.0) T2)
	    P7	(polar P6 (DTR 180.0) B)
	    P8	(polar P7 (DTR 270.0) T2)
	    P9	(polar P8 0 (/ (- B (+ T1 R1 R1)) 2))
	    P10	(polar P9 0 R1)
	    P11	(polar P10 (DTR 270.0) R1)
	    P12	(polar P11 (DTR 270.0) (- H (+ T2 T2 R1 R1)))
	    P13	(polar P12 (DTR 270.0) R1)
	    P14	(polar P13 (DTR 180.0) R1)
	    P15	(polar P14 (DTR 180.0) (/ (- B (+ T1 R1 R1)) 2))
	    P16	(polar P15 (DTR 270.0) T2)
	    P17	(polar P16 0 B)
	    P18	(polar P17 (DTR 90.0) T2)
	    P19	(polar P18 (DTR 180.0) (/ (- B (+ T1 R1 R1)) 2))
	    P20	(polar P19 (DTR 180.0) R1)
	    P21	(polar P20 (DTR 90.0) R1)
      (command "PLINE" P1 "W" "0.0" "0.0" P2 "ARC"
	       P4 "LINE" P5 P6 P7 P8 P9	"ARC"
	       P11 "LINE" P12 "ARC" P14 "LINE" P15 P16
	       P17 P18 P19 "ARC" P21 "LINE" P1 ""
      (prompt "\nRotation Angle: ")
      (command "ROTATE" "LAST" "" IP pause
      (setvar "OSMODE" OLDSNAP)
  );if userclick1
);if dialogshow
);defun BEAM

Phew, quite a mouthfull hey? Let's walk through it…

First, we define our degrees to radians function. We then start defining our main function and declaring our variables. (I have deliberatively left all variables global so that you can check their values once the programme has run.)

After storing then resetting some of our system variables we create a list of names that will appear in our list box.

Following this we load our dialogue box, setting up error checking to make sure that the dialogue file is loaded before continuing. If everything is OK we call start_list, add_list and end_list to create our list box. Here the MAPCAR function is useful for turning a raw AutoLISP list into a listbox display :

(start_list "selections") - Specify name of the list box. (KEY)
(mapcar 'add_list NAMES) - Specify the list. (NAMES)

The value returned by a list box tile is the INDEX of the selected item (i.e. first item = 0 ; second item = 1 ; third item = 2 ; etc.).

The action_tile function is one of the most critical functions to employ when giving your dialogue box some functionality. On it's own a DCL definition does nothing more than define a lifeless dialogue box. Take a look at the following code :

    "cancel"					;if cancel selected
    "(done_dialog) (setq userclick1 nil)"	;close dialog, set flag
  );action tile

Did you notice all the quotes around the lisp code? When you write an action_tile function for a DCL tile, your code is esentially telling the tile…

"Remember this string, then pass it back to me when the user activates you."

The string (i.e Anything within double-quotation marks) is dormant until the user picks the tile that is remembering your string. At that time the tile passes the string back to AutoLISP. AutoLISP then converts the string to functioning code and the code is executed.

Lets look at another code fragment. The following is the action_tile expression assigned to the OK button :

    "accept"					;if OK or double click
      "(progn (setq SIZA (atof (get_tile \"selections\")))" ;get size of beam
      "(done_dialog) (setq userclick1 T))"	;close dialog and set flag
  );action tile

When the user selects the OK button, the lengthy string assigned to the button is passed-back and turned into AutoLisp code that looks like this :

       (setq SIZA (atof (get_tile "selections")))
	 (setq userclick1 T)

This code does several things :

  • It retrieves the value of the INDEX from the list box.
  • It terminates the dialog box.
  • It assigns a value of T to the variable userclick1.

Now that everything is set and ready to go, we invoke the dialogue box.


Once it is on the screen, it controls the programme flow, until the user hits OK, Cancel or double-clicks on an item in the list box.

(unload_dialog dcl_id)

The user has made his selection so now we can unload the dialogue box.

For the rest of the program we are back to conventional AutoLISP code (this is basically self explanatory, especially if you have studied my earlier tutorials).

The program retrieves the name of the item selected from the list box by utilising the index (SIZA) of the selected item and the nth function.

(setq SIZA (nth SIZA NAMES))

It then searches the data file for the corresponding name, retrieves the list of dimensions, formats the list then stores the dimensions into individual variables.

(mapcar 'set '(H B T1 T2 R1) dlist)

The points required to draw the beam are then calculated using the POLAR function and the beam is drawn and rotated if required. Notice how this section of the coding is contained in a while loop to allow the user to draw more than one beam if he wishes. This saves him from having to re-run the entire routine again and again if he wants to draw multiple, identical beams.