tags:

views:

460

answers:

4

Hey guys!

I've built a form with Netbeans's visual editor. When I press one of the buttons it should do the following :

  • set it to disabled
  • perform a task that takes some time
  • when the task finishes the button will be enabled again

However, the following happens:

  • the button remains in a pressed state until the task finishes
  • when the task finishes, the enabling/disabling of buttons will be very fast (they will happen, but you won't notice them)

This behaviour is not something I want. I tried using repaint on the JButton, on the JFrame and even on the JPanel containing the button, but I can't seem to get it to do what I want. Some hints?

+8  A: 

When you do things in a button callback, you are essentially stalling the gui painting thread - not just for the button, but for ANY gui painting. (Try covering the interface with another window and then exposing it again - it won't repaint until the task is finished!)

What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to do the enabling of the button. invokeLater forces the button enable to happen in the gui painting thread.

You may want to set a busy cursor or otherwise lock the interface while the long-running thread is operating.

Paul Tomblin
Too bad we can't merge answers.
Allain Lalonde
@Allain, the important thing is making sure the accepted answer is as good as we can make it.
Paul Tomblin
+2  A: 

You need to do the task that takes some time in a different thread.

The reason the button is blocking is because the work is being done in the same thread that draws the button. Once the work is done the button can do the rest of what you tell it to.

If you use a different thread the thread will go do the task while the drawing code can continue drawing the form.

jjnguy
+13  A: 

When you do work in a button callback, you are stalling the GUI painting thread until it completes.

What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to update the UI when it completes. Not using invokeLater is not thread safe, and is generally bad mojo.

A basic example is:

button.setEnabled(false);
new Thread(new Runnable() {
 public void run() {
  // Do heavy lifting here
  SwingUtilies.invokeLater(new Runnable() {
   public void run() {
    button.setEnabled(true);
   }
  });
 }
}).start();
Allain Lalonde
The part with "button.setEnabled(true)" is taking place in the thread that does the work ? Should I pass the button to the thread ( in case I don't want to use anonymous classes ) ?
Geo
It's taking place in the GUI Thread. See http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)
Allain Lalonde
As for how much you dislike anonymous classes, that's up to you.
Allain Lalonde
You might want to put a try-finally in there too. Also it's more robust to calculate the new button state (in the EDT) rather than firing delayed messages with old state about the place.
Tom Hawtin - tackline
I forgot, the run methods. Thanks Tom. Goes to show you how dependent I am of my IDE :)
Allain Lalonde
So,the answer's yes? :)
Geo
This answer is lacking in explanation - feel free to copy some of the explanation from my answer.
Paul Tomblin
Done. Thanks Paul. Made it community while I was at it.
Allain Lalonde
You might as well consider using SwingWorker (part of Java6, but exists as Open Source library for Java5) which will make the source code ligher (threading and EDT issues are handled by SwingWorker itself).
jfpoilpret
+4  A: 

The Concurrency in Swing tutorial from Sun is well worth a read. Excellent explanation and background reading, including the event dispatching thread, using worker threads, etc

Rob