Search code examples
javamultithreadingswinguser-interfaceswingutilities

SwingUtilities invokeLater GUI update with JList


Good day,

I've read a few other stack overflow postings and other tutorials, but I can't get my GUI to update correctly after a button starts a long process. I've attached the full code of the problem that I am having. Notice if you run the code, the JList gets updated all at once at the end instead of every iteration of the for loop.

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class theframe extends JFrame implements ActionListener
{
    private JList list;
    private DefaultListModel listmodel;
    private JButton start;

    public theframe()
    {
        listmodel = new DefaultListModel();
        list = new JList(listmodel);
        start = new JButton("Start");

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500,500);
        setVisible(true);

        list.setPreferredSize(new Dimension(200,200));

        start.addActionListener(this);
        JPanel p = new JPanel();
        p.add(start);
        p.add(list);

        this.add(p);
    }

    public static void main(String[] args)
    {
        theframe frame = new theframe();

    }

    @Override
    public void actionPerformed(ActionEvent arg0)
    {
        if(arg0.getSource() == start)
        {
            for(int i=0;i<10;i++)
            {   
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // the JList should update one by one
                        listmodel.addElement("Start pushed ");
                    }
                });



                try
                {
                                    //This thread sleep simulates a long job
                    Thread.sleep(300);
                } 
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

Any help would be greatly appreciated.


Solution

  • Your problem here is the fact that you are calling the invokeLater method already from the EDT (Event Dispatching Thread).

    The method actionPerformed is called from the EDT so what happens is that the sleep call just stops the EDT itself: you can imagine that this is not how it should work, no EDT running means no GUI updates.

    Since it's a time consuming task you should implement it in a Thread/Runnable so that you can execute it in parallel and then call the invokeLater from this other thread.

    Something like:

    class LongProcess extends Thread {
      public void run() {
        for (int i = 0; i < 10; ++i) {
          SwingUtilities.invokeLater(...);
          Thread.sleep(300);
        }
      }
    }
    
    void actionPerformed(ActionEvent e) {
      LongProcess process = new LongProcess();
      process.start();
    }