lice! : GUI's in clojure

GUI's in clojure

There are some nice posts in the interwebs about how to use swing in clojure, it seems the syntax of clojure and the good java interop makes this pretty nice. Especially Stuart Sierra’s series has inspired me a lot since it is pretty details.

But in the end every posts leaves you with the feeling that you’re left alone with java when you really want to do something more then a tutorial. Creating objects, calling setters, getters using static fields. It is ugly to be frank, in his example Stuart did a great job to hide the javaness of grid layout from you, it’s a start but I think everything should be like that.

This is how it looks in the example:

(defn temp-app []
  (let [celsius (JTextField. 3)
        fahrenheit (JTextField. 3)
        panel (doto (JPanel. (GridBagLayout.))
                (grid-bag-layout
                 :gridx 0, :gridy 0, :anchor :LINE_END
                 :insets (Insets. 5 5 5 5)
                 (JLabel. "Degrees Celsius:")
                 :gridy 1
                 (JLabel. "Degrees Fahrenheit:")
                 :gridx 1, :gridy 0, :anchor :LINE_START
                 celsius
                 :gridy 1
                 fahrenheit))]
    (listen-temp celsius fahrenheit c-to-f)
    (listen-temp fahrenheit celsius f-to-c)
    (doto (JFrame. "Temperature Converter")
      (.setContentPane panel)
      (.pack)
      (.setVisible true))))

There is another nice post about the topic on Jeff Fosters blog showing this example:

(import '(javax.swing JFrame JLabel JTextField JButton JComboBox JPanel Timer)
       '(java.awt.event ActionListener)
       '(java.awt GridLayout))

(defn draw-sort [canvas alg]
 (prn alg))

(defn sortapp []
 (let [frame (JFrame. "Sort Visualizer")
 canvas (JPanel. true)
 algorithm-chooser (JComboBox.)
 run-button (JButton. "Run Algorithm")]
   (.addActionListener run-button
     (proxy [ActionListener] []
 (actionPerformed [evt]
  (draw-sort canvas (.toString (.getSelectedItem algorithm-chooser))))))
   (doto algorithm-chooser
     (.addItem "Quick sort")
     (.addItem "Bubble sort"))
   (doto frame
     (.setLayout (GridLayout. 2 2 3 3))
     (.add algorithm-chooser)
     (.add canvas)
     (.add run-button)
     (.setSize 300 300)
     (.setVisible true))))

So why am I showing this examples, someone has already? Because they inspired me to write clj-swing . Especially the second one, while being a good introduction to swing in clojure, is in my eyes very ugly. Taking all that above together adding a few macro mojo and some reading into swing you could get something like this instead:

(def sr (ref '["Quick sort" "Bubble Sort"]))
(def lm (ref '[]))

(defn grid-bag-sort-app []
  (frame :title "Sort Visualizer" :layout (GridBagLayout.) :constrains (java.awt.GridBagConstraints.) :name fr
   :show true :pack true
      [:gridx 0 :gridy 0 :anchor :LINE_END
       _ (label "Algorithms")
       :gridy 1
       _ (label "Button")
       :gridx 1 :gridy 0 :anchor :LINE_START
       algorithm-chooser (combo-box [] :model (seq-ref-combobox-model sr))
       :gridy 1
       _ (button "Run Algorithm" 
           :action ([event]
          (dosync (alter lm conj (selected-item algorithm-chooser)))))
       :gridx 3 :gridy 0 :gridheight 2 :anchor :CENTER
       _ (scroll-panel (jlist :model (seq-ref-list-model lm)) :preferred-size [150 100])]))

Why is that better? Well in my opinion there are some reasons. First of all There are only 2 java objects you really need to handle, aside of that all is clojury. that means you can further macrofy this if you want. Also it embraces clojures nice STM to swings MVC as a model.

(seq-ref-combobox-model sr)
;; and
(seq-ref-list-model lm)

wrap a ref that contains a sequence into a ugly java proxy so the list and combo box automatically reflect the content – all done for you in one call.

It’s by far not done but I hope it is a good start to make clojure a language that can be used nicely in GUI apps, something that sadly never happened for ruby…

Posted by Heinz N. 'Licenser' Gies Sun, 02 May 2010 06:00:00 GMT


Trackbacks

Use the following link to trackback from your own site:
http://blog.licenser.net/trackbacks?article_id=66