1

I have a GUI that I have developed that has a couple of buttons and a JTextArea for output. I have some code below that executes a function and updates the TextArea, however before the function is executed I would like the TextArea to be updated with the string "Processing...", just to give the user some kind of idea that the application is working as the function takes a while to execute.

At the moment this code doesn't update the GUI element and I understand why. The GUI doesn't get a chance to repaint in between the two commands that change the TextArea, so the "processing..." string is never displayed. How do I change the code so that the GUI element updates before the Main.featureAnalysisTop() function executes?

if(e.getActionCommand().equals("Extract Features"))
            {                   
                featuresTextArea.setText("Processing...");
                int nFeatures = nFeatureSlider.getValue(); 
                Main.featureAnalysisTop(nFeatures);

                featuresTextArea.setText("");
                ArrayList<String> featureList = Main.getFeatureList();

                for(String str : featureList)
                {
                        featuresTextArea.append(str + "\n");
                }

The GUI is executed in my main method using the following code.

public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    gui = new GUI2();
                    gui.setLocationRelativeTo(null);
                    gui.frmNeuralNetwork.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
  • Can you please post a complete example? Right now, I have no idea what `GUI2` is and where and when your "extract features" code is called. And please do not dump your whole application, try to come up with a small main method that shows what you want to achieve. Furthermore, you might want to have a look at this: [Updating GUI from a runnable](https://stackoverflow.com/questions/10365404/updating-gui-from-a-runnable) – Malte Hartwig Dec 19 '17 at 16:30
  • As your question title suggests you need to use a separate Thread for running the other task so you don't prevent the `Event Dispatch Thread (EDT)` from responding to events. The common way to do this is the use a `Swing Worker`. Read the section from the Swing tutorial on [Concurrency](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) for more information on the EDT and for an example of using a SwingWorker. – camickr Dec 19 '17 at 16:32

1 Answers1

1

Updating GUI on separate thread while other method is executing

That's a job for the Swing Woker class, which allows you to create a separate thread that runs in the background and lets you update your GUI accordingly.

For example, I made a simple example that accomplishes what you're trying to do.

First we create the GUI and add an ActionListener to our JButton where it starts our Swing Worker after updating the JTextArea's text to processing... as you were trying to do it and after 5 seconds (which simulates your long running task) it updates to I'm done!.

You can try it and change the code accordingly.

import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class TextAreaUpdater {
    private JFrame frame;
    private JTextArea area;
    private JButton button;
    private SwingWorker<String, String> worker;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new TextAreaUpdater()::createAndShowGui);
    }

    private void createAndShowGui() {
        frame = new JFrame(getClass().getSimpleName());

        area = new JTextArea(10, 30);

        button = new JButton("Update!");
        button.addActionListener(listener);

        worker = new SwingWorker<String, String>() {
            @Override
            protected String doInBackground() throws Exception {
                try {
                    Thread.sleep(5000); //Simulates long running task
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "I'm done!"; //Returns the text to be set on the JTextArea
            }

            @Override
            protected void done() {
                super.done();
                try {
                    area.setText(get()); //Set the textArea the text given from the long running task
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };

        frame.add(area, BorderLayout.CENTER);
        frame.add(button, BorderLayout.SOUTH);

        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    private ActionListener listener = (e -> {
        area.setText("Processing...");
        worker.execute(); //Initializes long running task
    });
}

References:

Here's another example that uses SwingWorker: update jlabel text after opening jdialog

Hope it helps

Frakcool
  • 10,915
  • 9
  • 50
  • 89