Making Swing Groovy, Part III: EDT

In this entry in my “Making Swing Groovy” series, I want to talk about threading issues. Specifically, how to work with the Event Dispatch Thread. As a step along the way, let me first respond to a comment made about my first post in this series.

Kirill Grouchnikov collects interesting Swing-related links every week. He was kind enough to link to my first post, and rightly pointed out that in my Java example, I’d make some errors. As he said,

“… the Swing example is not the best one, violating the EDT rules, working with the content pane and not centering the frame in the monitor, and the author admits some of these points.”

Let me say up front that I’m grateful for his comments. That’s how I learn. Part of the reason I post here (other than just the joy of sharing what I’ve learned) is to find out what I’ve been doing wrong, or, if I’m okay, what I can do better. One of the principles I live by is, “I’m often wrong, but I don’t stay wrong.” So keep those cards and letters coming. πŸ™‚

Here’s the code that Kirill addressed:

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class EchoGUI extends JFrame {
	private JTextField input = new JTextField(20);
	private JLabel prompt = new JLabel("Input text: ");
	private JLabel echo = new JLabel("Echo: ");
	private JLabel output = new JLabel();

	private Container cp = this.getContentPane();

	public EchoGUI() {
		super("Echo GUI");
		cp.setLayout(new GridLayout(0,2));  // 2 cols, as many rows as necessary

		cp.add(prompt);
		cp.add(input);
		cp.add(echo);
		cp.add(output);

		input.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				output.setText(input.getText());
			}
		});

		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setSize(300,100);
		setVisible(true);
	}

	public static void main(String[] args) {
		new EchoGUI();
	}
}

First, Kirill was right about my working with the content pane rather than adding my components directly to the JFrame instance. I remember way back in the Java 1.0 days (holy AWT, Batman), you added components directly to the instance of java.awt.Frame. Then, when Swing came along, you weren’t supposed to do that any more. Instead, you added to the content pane in front of the JFrame. That held true for Java 1.2, 1.3, and 1.4. Finally, in Java 1.5, you could go directly to the JFrame again, because now adding to the frame redirects to adding to the content pane. I need to retrain myself to do that.

Here is a link to a good post that explains why everything changed and then changed back.

Another of Kirill’s comments was that I didn’t center the frame on the monitor. I used to do that via code like

Toolkit tk = Toolkit.getDefaultToolkit();
Dimension screenSize = tk.getScreenSize();

From that I can get the width and height of the screen, and then using the width and height of the frame I can center everything. I can do that, but here it seems a bit like overkill.

Finally, we come to the real issue, which is also the subject of this post. As he said, I violated the EDT rules, and that’s bad.

Arguably Rule #1 of Swing GUI’s is “Only update GUI Elements in the Event Dispatch Thread (EDT).” From what I gather from frequent posts about that issue across the web, this is an error as common to Swing developers as forgetting to close database connections is when writing JDBC code. And again, this is one of those situations in Swing where the rules have changed multiple times, although maybe it’s more accurate to say that the rule hasn’t changed, but the way to implement it has.

First, some quick background. Every Swing program involves multiple threads. First, there are the initializing threads, which start up the GUI. In a Java application, that’s the main method. Then there is the Event Dispatch Thread, which maintains an Event queue that handles all the GUI updates, and finally there are any background threads that are used to manage long-running processes. If you make sure that non-GUI processes are off the EDT, then the GUI will remain responsive while the application is doing other work.

As of JDK 1.6, the standard library now has the class javax.swing.SwingUtilities, which contains the methods invokeLater and invokeAndWait. According to the concurrency lesson in the Swing tutorial, the proper way to create a GUI is

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        void run() {
            \\  ... instantiate the GUI ...
        }
    );
}

The invokeLater method creates the Runnable object and gives it to the EDT to add to the event queue. The alternative method invokeAndWait does the same thing, but it blocks until the task is completed.

What does Groovy bring to this picture? As usual, Groovy simplifies matters. Groovy adds three helpful methods to groovy.swing.SwingBuilder: edt, doLater, and doOutside.

What do they do? One of the best things about using an open source API is that, well, you have access to the source code. For the edt method, the implementation looks like (paraphrased):

public SwingBuilder edt(Closure c) {
    if (SwingUtilities.isEventDispatchThread()) {
        c.call(this)
    } else {
        // ...
       SwingUtilities.invokeAndWait(c)
       // ...
    }
}

In other words, if we’re in the EDT already, invoke the closure. If not, call invokeAndWait to get to the EDT.

Likewise, for doLater:

public SwingBuilder doLater(Closure c) {
    // ...
   SwingUtilities.invokeLater(c)
   // ...
}

and for doOutside:

public SwingBuilder doOutside(Closure c) {
    // ...
   Thread.start(c)
    // ...
}

So what’s the bottom line?

  • edt: use the EDT through invokeAndWait
  • doLater: use the EDT through invokeLater
  • doOutside: create a worker thread and start it (off the EDT)

Incidentally, SwingBuilder also has a build method specifically designed to construct the GUI. How does it handle threading?

public static SwingBuilder build(Closure c) {
    SwingBuilder builder = new SwingBuilder()
    return builder.edt(c)
}

That’s the whole method — create the buillder and then build the GUI on the EDT, synchronously. That’s why all of our previous examples of SwingBuilder, which only used the build method on the builder, ran correctly. They’re doing the initial construction on the EDT, as they should.

Ultimately, practical examples of Groovy Swing code uses actions that take advantage of these methods, as in

swing.actions() {
    action(name:'myMethod') {

        doOutside {
            model.prop = textField.text
            // ... other model property updates ...
            def ok = someLongRunningService(model)

            doLater {
                model.ok = ok
}  }   }  }

And there you have it. Grab the data and update the model outside the EDT, then run the long running application. When it’s finished, update the GUI in the EDT. (Note that here we’re updating the model in the EDT because the model properties are bound to the GUI elements, so changes in model properties will result in a GUI update. See my previous post in this series for details.)

From here it’s only a short step to Griffon.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

11 Responses to Making Swing Groovy, Part III: EDT

  1. lonny27 says:

    Nice post!
    Remark: javax.swing.SwingUtilities existed a long time before JDK 1.6 (I guess since Swing was introduced πŸ™‚

  2. Collin says:

    I’m not the best with Groovy but your code still concerns me. What is model in the last example? You say specifically “(Note that here we’re updating the model in the EDT because the model properties are bound to the GUI elements, so changes in model properties will result in a GUI update.” And yet the first line of your doOutside() code looks to be assigning a value to the model. [model.prop = textField.text]. Not only are you accessing a swing component form outside the EDT [textField.text] but you are also assigning a value to another swing connected object. Am I missing something special about groovy?

  3. Collin says:

    Also centering the window is one line of code.

    jframe.setLocationRelativeTo(null);

    As explained here in the documentation:

    http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Window.html#setLocationRelativeTo(java.awt.Component)

  4. Ken Kousen says:

    Thanks, Collin, for jframe.setLocationRelativeTo(null). I didn’t know about that one at all. So “if the component is not currently showing, or c is null, the window is centered on the screen.” I’ll definitely be using that in the future.

    As for the “model” referred to in my Groovy snippet, what I meant was a model like:

    import groovy.beans.Bindable
    
    class Model {
        String prop
        @Bindable boolean ok
    }
    

    which then is bound to the view:

    // ...
    label(text: bind(source:model, sourceProperty:'ok'))
    //...
    

    My understanding is that you can always read values from GUI elements off the EDT, you just don’t want to update them that way. So I don’t mind assigning model properties while reading from the GUI while I’m not on the EDT, as long as the resulting model isn’t bound to a GUI element that will update. So I’m assuming here that “prop” in the model is not bound to some GUI element, but I know that “ok” in the model is.

  5. Pingback: Swing links of the week: October 19 : Pushing Pixels

  6. Josh says:

    This is a great set of posts. Thanks for posting.

    Regarding the EDT point that Colin made and you responded to in last comment, wouldn’t it still be safer and just as valid to put the assignment immediately before the doOutside call though? That way you would not be making an assumption as to whether or not the model’s “prop” is bound to anything in the GUI (or every will be)?

  7. Pingback: Confluence: Team art of coding

  8. Pingback: Confluence: Team art of coding

  9. Pingback: Confluence: Team art of coding

  10. Pingback: Confluence: Team art of coding

  11. Pingback: Confluence: Team art of coding

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: