Rony
Rony

Reputation: 65

How to update swing GUI from inside a long method?

I'm new to Swing, and currently trying to develop a simple GUI application in NetBeans.

I want to create some kind of a GUI logging system, to write the current action that's being performed by the application, into a TextArea.

As a simple example, I created a JFrame form which contains only 2 objects: a "Start" button and a TextArea.

When the "Start" button is pressed, it invokes some kind of a lengthy method that should take some time (say, 10 seconds) to complete running, and while this method is running, I want to append text to the TextArea from withing this lengthy method (and of course I want the TextArea to be immediately updated).

My problem is that I cannot find the proper way of doing that. Anyway I tried doing this, when I press on the "Start" button, the application freezes for 10 seconds, without updating the TextArea like I wanted. Only when the method finishes, I see the update to the TextArea.

Here's a sample code:

private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {
    try {
        for (int i = 0; i < 10; i++) {
           textArea.setText(i + "\n");
            Thread.sleep(1000);
        }
    } catch (Exception e) {}
}

In this example what I expect to see is that once I click the button, for the next 10 seconds, a new line would be appended to the TextArea every second, like so:

1

2

3

4

...

But the real result that I get from this code is that the application freezes for 10 seconds, and finally the TextArea is updated and displays only the digit 9.

I've tried many different methods of doing this right, mainly using the SwingUtilities.invokeLater and SwingWorker methods, but none of them worked for me.

Any help with finding the right way of doing that would be greatly appreciated.

Upvotes: 2

Views: 5979

Answers (3)

Nick Rippe
Nick Rippe

Reputation: 6465

Like stated in other answers, you're going to want to use SwingWorker - I recommend reading up on it, as it is an incredibly useful tool to have. This implementation should give you what you're looking for in your sample:

Your button's action:

button.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent arg0) {
        new Worker().execute();
    }
});

Your swing worker:

public class Worker extends SwingWorker<String, String>{

    @Override
    protected String doInBackground() throws Exception {
        //This is what's called in the .execute method
        for(int i = 0; i < 10; i++){
            //This sends the results to the .process method
            publish(String.valueOf(i));
            Thread.sleep(1000);
        }
        return null;
    }

    protected void process(List<String> item) {
        //This updates the UI
        textArea.append(item + "\n");
    }
}

Upvotes: 6

Kumar Vivek Mitra
Kumar Vivek Mitra

Reputation: 33534

1. Always keep the UI work on the UI thread, and Non-UI work on the Non-UI thread.

2. In Java GUI applications, main() method is not long lived, after scheduling the construction of GUI in the Event Dispatcher Thread, the main() method quits...and Now its EDT's responsibility to handle the GUI.

3. Always keep your EDT thread, which is the GUI thread only for GUI work.

Eg:

public static void main(String[] args){


        EventQueue.invokeLater(new Runnable(){

              public void run(){

                myframe.setVisible(true);

             }

         }

  }

4. Create a separate Non-UI thread to handle that long time taking method.

5. You can simply use a Thread or use SwingWorker which is specially introduced into Java to sync the UI and Non-UI thread.

Upvotes: 8

mre
mre

Reputation: 44240

Use the javax.swing.Timer class. For more information, see How to Use Swing Timers.

Upvotes: 3

Related Questions