Ltk:電卓アプリ(ボタン応用) - lisp-cookbook-ja/common-lisp GitHub Wiki

(ql:quickload :ltk)

;; http://kaolin.unice.fr/STk/STk.html
;; STk-4.0.1/Demos/calc.stklos を参考にLTkへ移植
(in-package :ltk-user)

(defun digit? (s)
  (or (parse-integer s :junk-allowed T)
      (string= s ".")))

(defclass screen (entry)
  ;;LTkのentryでは -textvariable は直接アクセスさせないようなので、
  ;; screenというラッパークラスを定義
  ((result :initform 0 :accessor result)
   (previous-action :initform "" :accessor previous-action)
   (acc :initform 0 :accessor acc)
   (operator :initform #'+ :accessor operator)))

(defmethod (setf result) :after (val (obj screen))
  (setf (text obj) (slot-value obj 'result)))

(defmethod value ((obj screen))
  (read-from-string (text obj) nil 0))

(defmethod execute-action ((obj screen) str)
  (with-accessors ((operator operator)
                   (result result)
                   (acc acc)
                   (previous-action previous-action)
                   (value value)
                   (text text))
                  obj
    (cond ((string= str "Off")  (setq *exit-mainloop* T))
          ((string= str "Sqrt") (setq result (sqrt value)))
          ((string= str "C")    (setq result 0))
          ((string= str "/")    (setq operator #'/))
          ((string= str "*")    (setq operator #'*))
          ((string= str "-")    (setq operator #'-))
          ((string= str "+")    (setq operator #'+))
          ((string= str "+/-")  (setq result (- value)))
          ((string= str "=")
           (setf result (funcall operator acc value)))
          ('ELSE
           (setq result (if (digit? previous-action)
                            (concatenate 'string text str)
                            (progn
                              (setq acc value)
                              str)))))
    (setq previous-action str)))

(defun calc ()
  (with-ltk ()
    (let ((screen (make-instance 'screen :text "0"))
          ;; Rows is a vector of 5 frames
          (rows (map-into (make-sequence 'vector 5)
                          (lambda () (make-instance 'frame)))))
      (mapc (let ((count 0))
              (lambda (text)
                (pack (make-instance 'button
                                     :text text 
                                     :master (aref rows (floor count 4))
                                     :width  6
                                     :command (lambda () 
                                                (execute-action screen text)))
                      :side :left :padx 4 :pady 2)
                (incf count)))
            '("Off"  "Sqrt"  "C"  "/"
              "7"    "8"    "9"  "*"
              "4"    "5"    "6"  "-"
              "1"    "2"    "3"  "+"
              "0"    "."   "+/-" "="))
      (pack screen :fill :x :padx 5 :pady 5 :ipadx 5 :ipady 5)
      (map nil 
           (lambda (row)
             (pack row :fill :x))
           rows))))

;; 実行
(calc)

参考