Search code examples
javaswingjprogressbar

JProgress Bar Not Updating Within SwingWorker


I apologize for adding to the many JProgressBar update questions, but this has really been driving me crazy.

I have a JProgressBar being updated via a SwingWorker yet I'm still having the typical problem of the bar being updated after completion.

Here is the relevant code:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.*;

public class SwingTest {

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

    public static void init() {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        JProgressBar bar = new JProgressBar();

        JButton button = new JButton("Start");
        button.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                bar.setMaximum(100);
                bar.setValue(0);

                SwingWorker worker = new SwingWorker<String, Void>()
                {
                    @Override
                    public String doInBackground()
                    {
                        int val = 0;
                        // +1 to make inclusive
                        for (int i = 0; i < 100; i++)
                        {
                            try
                            {
                                Thread.sleep(50);
                            } catch (InterruptedException e)
                            {
                                e.printStackTrace();
                            }

                            setProgress(++val);
                        }

                        return "Hello SwingWorker World";
                    }

                    @Override
                    public void done()
                    {
                    }
                };

                worker.addPropertyChangeListener(
                        new PropertyChangeListener()
                        {
                            public  void propertyChange(PropertyChangeEvent evt)
                            {
                                if ("progress".equals(evt.getPropertyName()))
                                {
                                    bar.setValue((Integer)evt.getNewValue());
                                }
                            }
                        }
                );

                worker.execute();

                try
                {
                    System.out.println("Return: " + worker.get().toString());
                } catch (InterruptedException e1)
                {
                    e1.printStackTrace();
                    System.out.println("Failed.");
                } catch (ExecutionException e1)
                {
                    e1.printStackTrace();
                    System.out.println("Failed.");
                }
            }
        });

        panel.add(button);
        panel.add(bar);

        frame.add(panel);
        frame.pack();
        frame.setSize(200,90);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

If the problem is likely due to another section I will edit my question with more code if needed.

Thanks in advance.

EDIT: Stripped down code to a minimal product.


Solution

  • the problem in the code you've posted is when you start the SwingWorker, you then immediately wait for it to run. This blocks the event thread, which prevents the display from being updated:

    worker.execute(); // This starts the thread in the background
    
    try
    {
         // This immediately stops the event handler waiting for the worker to finish
        System.out.println("Return: " + worker.get().toString());
    } catch (InterruptedException e1)
    {
        e1.printStackTrace();
        System.out.println("Failed.");
    } catch (ExecutionException e1)
    {
        e1.printStackTrace();
        System.out.println("Failed.");
    }
    

    If you comment out the entirety of that try-catch block, it will run and update the display.

    If you need to wait for the result of the worker, then you should wait for the results on another thread, and allow the UI thread to continue.