JProgressbar doesn’t display when invoked from an ActionListener

I have a generic Java class for a simple progress indicator. It works fine if invoked directly (e.g. from main() or from a class constructor). But I can’t get it to work when I create a simple menu and invoke it from an ActionListener associated with a menu item.

Here’s the generic progress indicator class:

import java.awt.FlowLayout;

import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;

public class jpcProgressIndicator {
    final int MAX = 100;
    private JFrame frame = new JFrame("Progress Indicator");

    // creates progress bar
    final JProgressBar pb = new JProgressBar();
    final JTextField tf = new JTextField();
    
    public jpcProgressIndicator() {
        pb.setMinimum(0);
        pb.setMaximum(MAX);
        pb.setStringPainted(true);

        // add progress bar
        frame.setLayout(new FlowLayout());
        frame.getContentPane().add(pb);
        // frame.getContentPane().add(tf);

        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.setSize(300, 70);
        frame.setLocationRelativeTo(null);      
    }
    
    public void Update( int nValue) {
        pb.setValue(nValue);
    }
    
    public void Show( boolean flag) {
        if(flag == true) {
            this.frame.setVisible(true);
            this.pb.setVisible(true);
        } else {
            this.pb.setVisible(false);
        }
    }
    public int getValue() {
        return this.pb.getValue();
    }

    public void setTitle(String strTitle) {
        this.frame.setTitle(strTitle);
    }
    
    public void Close() {
        frame.dispose(); 
    }
}

The test program from which I’m invoking this is:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import SurveySystem.Data.jpcProgressIndicator;
 
public class Main2 implements ActionListener {
    final int MAX = 100;
    jpcProgressIndicator pi;    
    JFrame frame;    
    JMenuBar mb;
    JMenuItem myMenuItem;
    
    Main2() {
        // Process();
        
        // create the basic frame window for a menu
        frame=new JFrame("Survey System");
        // Create a menu item
        myMenuItem = new JMenuItem("Process");
        // add an actionlistener for this menu item
        myMenuItem.addActionListener(this);
        // create a menu bar
        mb=new JMenuBar();
        // add menu item to menu bar
        mb.add(myMenuItem);             
        // add the menu bar to the frame
        frame.add(mb);
        
        frame.setJMenuBar(mb);
        // set frame size
        frame.setSize(800,400);     
        // center the frame on the screen
        frame.setLocationRelativeTo(null);
        // make the frame visible
        frame.setVisible(true);
    }
 
    public static void main(String[] args) {
        new Main2();
    }
    
    private void Process() {
        pi = new jpcProgressIndicator();
        pi.setTitle("Progress of Growth & Persistency");
        pi.Update(0); // initialize
        pi.Show(true);

        int nCounter = 0;
        // for (int i = 0; i <= MAX; i++) {
        while (nCounter < 100 ) {
            // final int currentValue = i;
            final int currentValue = nCounter;

            try {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        pi.Update(currentValue);
                    }
                });
                java.lang.Thread.sleep(100);
            } catch (InterruptedException e) {
                System.out.println("Error: " + e.getMessage());
            }
            nCounter++;
        }
        pi.Close();     
    }

    @Override
    public void actionPerformed(ActionEvent e) {        
        if(e.getSource() == myMenuItem)         
            Process();      
    }
}

The test method Process loops and updates the progress bar. I have commented out an invocation that, if uncommented, shows that all the code is working. But if I invoke it from the menu item, the frame appears but not the progress bar.

Anyone know what I’m doing wrong?

Answer

actionPerformed is actually executed by EDT, so all invokeLater calls are queued and called after the pi.Close so that you can’t see the progressbar update.

Here is how I modified your Process method to “take the counter out of EDT”:

private void Process() {
    jpcProgressIndicator pi = new jpcProgressIndicator();
    pi.setTitle("Progress of Growth & Persistency");
    pi.Update(0); // initialize
    pi.Show(true);

    Thread t = new Thread(new Runnable(){
        public void run() {

            int nCounter = 0;
            // for (int i = 0; i <= MAX; i++) {
            while (nCounter < 100 ) {
                // final int currentValue = i;
                final int currentValue = nCounter;

                try {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            pi.Update(currentValue);
                        }
                    });
                    java.lang.Thread.sleep(100);
                } catch (InterruptedException e) {
                    System.out.println("Error: " + e.getMessage());
                }
                nCounter++;
            }
            pi.Close();     
        }
    });
    t.start();

}

Leave a Reply

Your email address will not be published. Required fields are marked *