This page is hosted on AFS file server space, which is being shut down on November 13, 2018. If you are seeing this message, your service provider needs to take steps now. Visit afs.unc.edu for more information.

Psych 285: Computational Statistics
and Statistical Visualization

Professor Forrest Young

LispStat Programming Examples

These examples follow Luke Tierney's Chapter 7 closely.


  • Chapter 7 - Menu & Dialog Programming
  • Menus

    Creating menus is very straight-forward. There are four simple steps: First, create an instance of a menu object. Second, create instances of menu-item objects. Third connect the menu-items with the menu. Fourth, install the menu, with its items, in the menu bar. These steps are followed here.

    Before creating the menu, however, we prepare a regression model for the menu that will be created.

    
    (load "tutorial")
    (setf reg-model 
        (regression-model (list hardness tensile-strength)
                                              abrasion-loss))
    
    
    Here are the four steps for creating a menu.

    1. Create a menu object
    2. 
      (setf model-menu (send menu-proto :new "Model"))
      

    3. Create three menu-item objects
    4. 
      (setf display-item
            (send menu-item-proto :new "Print Summary"
                  :action #'(lambda ()
                              (send reg-model :display))))
      
      (setf plot-item
            (send menu-item-proto :new "Plot Residuals"
                  :action #'(lambda ()
                              (send reg-model :plot-residuals))))
      
      (setf intercept-item
            (send menu-item-proto :new "Intercept"
                  :action #'(lambda ()
                              (send reg-model :intercept
                                    (not (send reg-model :intercept))))))
      
      (defmeth intercept-item :update ()
        (send self :mark (send reg-model :intercept)))
      

    5. Append the menu-items to the menu. Include a dash item.
    6. 
      (send model-menu :append-items
            intercept-item
            (send dash-item-proto :new)
            display-item
            plot-item)
      
      

    7. Install the menu with its items
    8. 
      (send model-menu :install)
      
      
      You can remove a menu with
      
      (send model-menu :remove) 
      

    Standard Dialogs

    The standard dialogs included with LispStat are very simple to use. If these don't suffice, it is possible to create your own custom dialog box, though this is a bit trickier.

    1. The standard modal dialogs
    2. 
      (message-dialog "There is no variable named X.")
      
      (ok-or-cancel-dialog
       (format nil "There is no variable named X.~%~
                    Do you want to try another variable?"))
      
      (ok-or-cancel-dialog
       (format nil "There is no variable named X.~%~
                    Do you want to try another variable?") nil)
      
      (choose-item-dialog "Dependent Variable:" '("Y0" "Y1" "Y2"))
      
      (choose-item-dialog "Dependent Variable:" '("Y0" "Y1" "Y2") :initial 2)
      
      (choose-subset-dialog "Dependent Variable:" '("Y0" "Y1" "Y2"))
      
      (choose-subset-dialog "Dependent Variable:" '("Y0" "Y1" "Y2") :initial '(0 2))
      
      (get-string-dialog "Name of the Dependent Variable:" :initial "Y")
      
      (get-value-dialog "Name of the Dependent Variable:" :initial (+ 1 3))
      
      

    3. The standard modeless slider dialogs
    4. 
      (sequence-slider-dialog
       (iseq 100 150)
       :action #'(lambda (x) (format t "Current element: ~s~%" x))
       :title "Value Sequence")
      
      (interval-slider-dialog '(1 20))
      
      (interval-slider-dialog '(1 20) :points 20)
      
      

    Custom Dialogs

    There are three steps involved in constructing custom dialogs: 1) create instances of the desired dialog items; 2) create an instance of the dialog box with its list of dialog items specifying the layout of the box's items; 3) run the dialog box. This is straight-forward, although the action methods for the buttons are a bit tricky to figure out.

    The several types of dialog items are outlined in the chapter. The specific example given here is based on the one in the chapter, but I think it is structured better. Here are the steps for the example in the chapter:

    1. Create instances of the desired dialog items
    2. 
      (setf x-label   (send text-item-proto   :new "X Variables"))
      (setf y-label   (send text-item-proto   :new "Y Variables"))
      (setf x-item-0  (send toggle-item-proto :new "X0"))
      (setf x-item-1  (send toggle-item-proto :new "X1"))
      (setf x-item-2  (send toggle-item-proto :new "X2"))
      (setf y-item    (send choice-item-proto :new (list "Y0" "Y1" "Y2") :value 1))
      (setf intercept (send toggle-item-proto :new "Intercept" :value t))
      (setf prompt    (send text-item-proto   :new "Name"))
      (setf name      (send edit-text-item-proto :new "" :text-length 8))
      (setf cancel    (send modal-button-proto :new "Cancel"))
      (setf ok (send modal-button-proto :new "OK"
                       :action #'(lambda () (collect-values))))
      (defun collect-values ()
          (list (send name :text)
                (send y-item :value)
                (which (list (send x-item-0 :value)
                             (send x-item-1 :value)
                             (send x-item-2 :value)))
                (send intercept :value)))
      
      
      

    3. Create an instance of a dialog box with the desired item layout
    4. 
       (setf reg-dialog
            (send modal-dialog-proto :new
                  (list
                   (list
                    (list y-label y-item intercept)
                    (list x-label x-item-0 x-item-1 x-item-2))
                   (list prompt name)
                   (list ok cancel))
                  :default-button ok)) ; note undocumented feature!
      
      

    5. Run (install) the dialog box
    6. 
      (send reg-dialog :modal-dialog)
      

    7. To make this completely object-oriented, we can follow the steps above changing the definition of the ok button and defining a :collect-values method for the OK button instance
    8. 
      (setf x-label   (send text-item-proto   :new "X Variables"))
      (setf y-label   (send text-item-proto   :new "Y Variables"))
      (setf x-item-0  (send toggle-item-proto :new "X0"))
      (setf x-item-1  (send toggle-item-proto :new "X1"))
      (setf x-item-2  (send toggle-item-proto :new "X2"))
      (setf y-item    (send choice-item-proto :new (list "Y0" "Y1" "Y2") :value 1))
      (setf intercept (send toggle-item-proto :new "Intercept" :value t))
      (setf prompt    (send text-item-proto   :new "Name"))
      (setf name      (send edit-text-item-proto :new "" :text-length 8))
      (setf cancel    (send modal-button-proto :new "Cancel"))
      (setf ok (send modal-button-proto :new "OK"
                       :action #'(lambda () (send self :collect-values))))
      (defmeth ok :collect-values ()
          (list (send name :text)
                (send y-item :value)
                (which (list (send x-item-0 :value)
                             (send x-item-1 :value)
                             (send x-item-2 :value)))
                (send intercept :value)))
      (setf reg-dialog
            (send modal-dialog-proto :new
                  (list
                   (list
                    (list y-label y-item intercept)
                    (list x-label x-item-0 x-item-1 x-item-2))
                   (list prompt name)
                   (list ok cancel))
                  :default-button ok))
      
      (send reg-dialog :modal-dialog)
      

    A custom dialog function

    My preferred approach is to write a single function (or, if this dialog is part of an object, a single method) that presents the dialog. Here is how you would rewrite the code above to be a single function that constructs and runs the dialog box, and returns the values from the items in it.
    
    (defun dialog-box ()
      (let* ((x-label   (send text-item-proto   :new "X Variables"))
             (y-label   (send text-item-proto   :new "Y Variables"))
             (x-item-0  (send toggle-item-proto :new "X0"))
             (x-item-1  (send toggle-item-proto :new "X1"))
             (x-item-2  (send toggle-item-proto :new "X2"))
             (y-item    (send choice-item-proto :new
                              (list "Y0" "Y1" "Y2") :value 1))
             (intercept (send toggle-item-proto :new "Intercept" :value t))
             (prompt    (send text-item-proto   :new "Name"))
             (name      (send edit-text-item-proto :new "" :text-length 8))
             (cancel    (send modal-button-proto :new "Cancel"))
             (ok        (send modal-button-proto :new "OK"
                              :action #'(lambda () (send self :collect-values))))
             (the-dialog (send modal-dialog-proto :new
                               (list
                                (list
                                 (list y-label y-item intercept)
                                 (list x-label x-item-0 x-item-1 x-item-2))
                                (list prompt name)
                                (list ok cancel))
                               :default-button ok))
             )
    	  (defmeth ok :collect-values ()
         (list (send name :text)
               (send y-item :value)
               (which (list (send x-item-0 :value)
                            (send x-item-1 :value)
                            (send x-item-2 :value)))
               (send intercept :value)))
        (send the-dialog :modal-dialog)))
    
    (dialog-box)