Clojure and Seesaw

There’s a very nice desktop graphics library called Seesaw for Clojure. You can see a lot of examples of how to use it inside seesaw/test/examples folder of the distribution or you can browse online at GitHub.

I played with it – here’s a small LED-matrix digital clock implementation. On Ubuntu 11.10 it looks like this:

Here’s the code:

(ns com.icyrock.clojure.seesaw.led-matrix
  (:import [ java.util Calendar])
  (:use seesaw.core
        seesaw.graphics))

(def lcd-dot-style-off
  (style
   :background "#181818"
   :stroke (stroke :width 3)))

(def lcd-dot-style-on
  (style
   :background "#00bc00"
   :stroke (stroke :width 3)))

(def lcd-dot-styles
  {false lcd-dot-style-off
   true lcd-dot-style-on})

(defn draw-lcd-dot [g width height is-on]
  (let [dot-style (lcd-dot-styles is-on)
        border (-> dot-style :stroke .getLineWidth)
        border2 (* 2 border)]
    (draw g
          (ellipse border border (- width border2) (- height border2)) dot-style)))

(def lcd-symbol-dots
  {\0
   [".***."
    "*...*"
    "*...*"
    "*...*"
    "*...*"
    "*...*"
    ".***."]
   \1
   ["..*.."
    ".**.."
    "..*.."
    "..*.."
    "..*.."
    "..*.."
    "*****"]
   \2
   [".***."
    "*...*"
    "....*"
    "...*."
    "..*.."
    ".*..."
    "*****"]
   \3
   [".***."
    "*...*"
    "....*"
    "..**."
    "....*"
    "*...*"
    ".***."]
   \4
   ["...*."
    "..**."
    ".*.*."
    "*..*."
    "*****"
    "...*."
    "...*."]
   \5
   ["*****"
    "*...."
    "****."
    "....*"
    "....*"
    "*...*"
    ".***."]
   \6
   [".***."
    "*...*"
    "*...."
    "****."
    "*...*"
    "*...*"
    ".***."]
   \7
   ["*****"
    "....*"
    "...*."
    "..*.."
    "..*.."
    "..*.."
    "..*.."]
   \8
   [".***."
    "*...*"
    "*...*"
    ".***."
    "*...*"
    "*...*"
    ".***."]
   \9
   [".***."
    "*...*"
    "*...*"
    ".****"
    "....*"
    "*...*"
    ".***."]
   \:
   ["....."
    "....."
    "..*.."
    "....."
    "..*.."
    "....."
    "....."]
   })

(defn draw-lcd-symbol [g width height symbol]
  (let [dots (lcd-symbol-dots symbol)
        dot-width (/ width (count (first dots)))
        dot-height (/ height (count dots))]
    (doseq [row dots]
      (doseq [cell row]
        (draw-lcd-dot g dot-width dot-height (= cell \*))
        (translate g dot-width 0))
      (translate g (- width) dot-height))))

(defn get-time-string []
  (let [ c (Calendar/getInstance)
        h (.get c Calendar/HOUR_OF_DAY)
        m (.get c Calendar/MINUTE)
        s (.get c Calendar/SECOND)]
    (format "%02d:%02d:%02d" h m s)))

(defn paint-lcd-symbol [ c g]
  (try
    (let [symbols (get-time-string)
          symbol-count (count symbols)
          width (.getWidth c)
          height (.getHeight c)
          symbol-width (/ width symbol-count)]
      (doseq [symbol symbols]
        (push g
              (draw-lcd-symbol g (- symbol-width 20) height symbol))
        (translate g symbol-width 0)))
    (catch Exception e
      (println e))))

(defn content-panel []
  (border-panel
   :center (canvas :id :clock
                   :background "#000000"
                   :paint paint-lcd-symbol)))

(defn make-frame []
  (let [f (frame :title "com.icyrock.clojure.seesaw.led-matrix"
                 :width 1200 :height 300
                 :on-close :dispose
                 :visible? true
                 :content (content-panel))]
    (.setLocation f (java.awt.Point. 100 300))
    (timer (fn [e] (repaint! (select f [:#clock])) 1000))))

(defn -main [& args]
  (native!)
  (make-frame))
(-main)

You can find the code at com.icyrock.clojure GitHub repository – just clone and fire up in your favorite IDE.