Tuesday 11 October 2011

Click Counter

The next example comes also from the Swing Tutorial.  Here we have a button and a label.  The button click adds one to a counter, and the label shows the number on the counter, so you can see how often you have clicked the button.

This is the code:-

(ns clojurecorner
  (:import

      (javax.swing JButton JPanel JFrame JLabel SwingUtilities)
      (java.awt.event ActionListener)
      (java.awt GridLayout)))


(def click-counter (atom 0))

(defn init-gui []
  (. SwingUtilities invokeLater
    (proxy [Runnable] []
      (run []
        (let
          [frame (JFrame. "Swing application")
          button (JButton. "I'm a Swing button!")
          label (JLabel. "Number of click")]
          (.addActionListener button
             (proxy [ActionListener] []
               (actionPerformed [e]
                 (.setText label

                   (str "Clicks: " (swap! click-counter inc))))))
          (doto frame
            (.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
            (.setLayout (GridLayout. 0 1))
            (.add button)
            (.add label)
            (.setSize 300 200)
            (.setVisible true)))))))


(JFrame/setDefaultLookAndFeelDecorated true)
(init-gui)


The line (def click-counter (atom 0)) creates an atom - a repository for our click counter - sets the initial value to zero, and gives it the name click-counter.

The line (swap! click-counter inc)) updates the value of the counter atom with the result of applying the function inc to the current value, and it returns the new value - a thread-friendly way to maintain the counter.

The code that handles the click event is wired up to the button via another proxy implementing an interface that supports a single method, like the code that creates the gui - in this case a proxy implementing ActionListener by providing a method actionPerformed.

Stuart Sierra on Digital Digressions has pointed out that this pattern with a single abstract method is going to be common enough to warrant a macro.  Yes I'll try that next week.  Anyway, this is how the application looks:


Monday 3 October 2011

Hello to GUI

The next example shows the minimum possible graphical user interface application. It consists just of a window with the words “Hello World” written in it. This is based on the first example in the Java Swing Tutorial. Here is the Clojure code:

(ns clojurecorner
  (:import (javax.swing JFrame JLabel SwingUtilities)))

(defn create-and-show-gui []
  (. SwingUtilities invokeLater
    (proxy [Runnable] []
      (run []
        (JFrame/setDefaultLookAndFeelDecorated true)
        (let [frame (JFrame. "Hello World from Swing")
              label (JLabel. "Hello World")]
          (.setDefaultCloseOperation frame JFrame/EXIT_ON_CLOSE)
          (.add (.getContentPane frame) label)
          (.pack frame)
          (.setVisible frame true))))))

(create-and-show-gui)


There are various new things happening here. Well, new to me.  First, we have imported some items from the Swing library: JFrame is the object that produces the window on the desktop, and JLabel will support the label that we will put in the frame. SwingUtilities provides essential code to run the application.

The example consists mostly of the call to the function create-and-show-gui. Now, to make this work properly we want this code to be executed in the event dispatch thread, not in our forground thread. We achieve this by passing the code to the function invokeLater in the Swing object SwingUtilities. This function requires an object that implements the interface Runnable, which essentially means it has to provide a function run(). To pass our code to the Java routines we bundle it up into a proxy: the Clojure function proxy takes, first, the class or interface to be represented (here it's Runnable), then any parameters required by a superclass (none here), and then the function or functions to be implemented – in this case the function run.

So the code in run is executed via the call to SwingUtilities.invokeLater() - this means that our main code calls this and then proceeds, allowing Swing to run the code to create the window in its own time in its own thread.

Within the function run we start by switching on the default look and feel for the GUI – that is to say the default Swing appearance. Without this the interface will get your native GUI appearance. It will still work like that, of course, it's just a matter of how you want it to look.

Next we create a frame object from the class JFrame and a label object from the class JLabel.

The next line sets the close operation to halt the program – this means that when you click the close box in the frame the application will actually close as you would expect, not just sit there.

The call to function .add inserts the label into the content area of the label, so the text will appear in the window.

The call to .pack makes the components shuffle into position and then the call to setVisible allows the window to appear on the screen.

When it appears it is in its tiniest area, but you can drag it around and open it up to read what the label says.  So it does the minimum that a GUI window needs to do.  You have to start somewhere.