javajprogressbar

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?


Solution

  • 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();
    
    }