File Dialogs in Clojure

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

When dealing with files, its very handy to use file dialogs. Here’s a quick example of using a JFileChooser  to select a file to slurp  and print.

Note the use of  (into-array [“txt”]) as an argument to the FileNameExtensionFilter constructor. This is because the file extensions are provided as a String...  meaning a java.lang.String  array.

Also note, the .showOpenDialog  call uses nil  instead of a JFrame . We do that because we don’t have a JFrame  showing that we want to associate the file dialog with.

 

Clojure JOptionPane

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

Every complex GUI uses message dialogs. When using Clojure Swing interop, you will use the JOptionPane  for your message dialogs. Two keys to remember when using JOptionPane s, is that they should always be called from within the Event Dispatch Thread (EDT). Sometimes the JOptionPane  is shown by an ActionListener  that is already on the EDT, and sometimes you need to call invokeAndWait , because your current thread is not the EDT.

The other key point to remember with JOptionPane s is that you can associate the message dialog with a frame, or set the frame to nil . If a JOptionPane ‘s frame is set to nil , then the message dialog will center itself on the screen when it displays. If a JFrame  is provided to the JOptionPane , then the message dialog will center itself on the JFrame  it is associated with.

Here is a basic example that provides a button to click, and that displays a plain JOptionPane  with a message. The ActionListener  is called from the EDT, so the JOptionPane  is being called from the EDT.

Here is an example that shows a JOptionPane  that displays from a normal thread. The EDT must be explicitly called with invokeAndWait . The four most common types of message dialogs are shown.

Clojure Example Swing App

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

Here is a full app demonstrating use of Swing from Clojure. Toy apps usually gloss over import code. For example, real Swing requires proper use of the event thread, and combining multiple layout types to get more complex behavior.

Clojure Swing Temperature Conversion App
An example app using Clojure Swing to convert temperatures.

Here’s the full source.

Note that #(<some UI code>)  is used to pass in a function to the event dispatch thread, otherwise you have null pointer exceptions due to the code being executed BEFORE it is passed to the event dispatch thread.

Clojure ActionListener Interop

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

When working with UI elements in Clojure Swing, your “go to” event handler is the ActionListener  found in the java.awt.event  package. This is a one-size-fits-all handler that works for many general situations where you just need to know **something** happened.

The fist hurdle to overcome is what Clojure technique should you use to implement the ActionListener  interface? The correct answer is reify .

Reify gives you an anonymous implementation of an interface. It is perfect for implementing Java event interfaces that are rarely named once created. Reify takes the interface name followed by method implementations as it’s arguments. The one trick that may throw off seasoned Java programmers implementing methods in Clojure’s reify, is that the first argument to functions used to implement methods is always an explicit this . this  is usually hidden in Java implementations of methods. It’s there, just not visible.

For example, here is reify implementing an ActionListener for a typical JButton  called my-button .

Here’s an example of a JFrame containing a JButton that prints “Clicked” to the console when clicked.

JFrame with JButton running in Clojure
JFrame with JButton running in Clojure

Here is the full source code.

 

Clojure Swing JFrame Introduction

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

JFrame s are the root windows of all modern Clojure Swing applications. In the old days, there were other options, but polite society does not speak of them.

The visible components that you use in your JFrame  are added to the content pane of the JFrame . You request the content pane and then add your components to that content frame.

Clojure JFrame Example
Clojure JFrame Example

Before adding components to a content pane, you need to decide one which of several layouts you will use for arranging your components. BorderLayout  is the default, so we’ll stick with that one for this simple example.

Border Layouts are divided into NORTH , SOUTH , EAST , WEST , and CENTER . We are going to use one label for each of the five locations in the content pane’s BorderLayout . We center the text and change the colors as needed so it is easy to see the locations of each label in the BorderLayout .

We set the size of the JFrame  to 600  pixels wide by 400  pixels tall, then set the JFrame  to visible equals true .

Note that we do all our layout in the event dispatch thread by using invokeLater .

Clojure Swing: Invoke Later

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

When developing Swing GUIs for Clojure, understanding and using SwingUtilities/invokeLater  is crucial.

Event Dispatch Thread

Don’t confuse “thread” in this case with Clojure threading macros such as ->  and --> . Threading here refers to concurrency.

Swing GUI components are not safe to access concurrently. If two threads modify the same Swing component at the same time, the result is unpredictable. Java solved this problem by introducing the event dispatch thread. All modification Swing displays and component values should happen on the event dispatch thread.

There is only one event dispatch thread. You access it by creating a Java Runnable and passing it to SwingUtilities/invokeLater . Lucky for us, all Clojure functions are Java Runnables. Here’s the general format.

Warning

Remember, ALL the screen rendering and modifications to your Swing components happen on the event dispatch thread. That means, if you tie up the event dispatch thread doing something time consuming, your app will appear to hang. This creates a horrible user experience. Any runnables you create should complete quickly.

On a somewhat related note, any time a Swing component alerts you to an event or a value change, you will want to hand back that thread immediately and use a new thread to do any long tasks associated with the notifications.

Clojure GUI using Swing

(See my full list of Clojure Swing examples and tutorials at Clojure Swing Interop)

Here’s a really brief example for building a Java Swing app using Clojure.

Simple Clojure Swing Example
Simple Clojure Swing Example