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)