Search code examples
javaswingprogressmonitor

Swing ProgressMonitor not working


I am trying to learn ProgressMonitor in Java Swing. I created this simple test code -

public class ProgressMonitorTest extends JFrame 
{
private JPanel contentPane;
private ProgressMonitor progressMonitor;
private JButton button;
private static ProgressMonitorTest frame;
private static boolean isFrameReady;

public JButton getButton()
{
    return button;
}

public ProgressMonitor getProgressMonitor()
{
    return progressMonitor;
}

/**
 * Launch the application.
 */
public static void main(String[] args) 
{
    EventQueue.invokeLater(new Runnable() 
    {
        public void run() 
        {
            try 
            {
                frame = new ProgressMonitorTest();
                frame.setVisible(true);
                isFrameReady = true;
            } 
            catch (Exception e) 
            {
                e.printStackTrace();
            }
        }
    });

    while(!isFrameReady)
    {
        //
    }

    frame.getButton().addActionListener(new ActionListener() 
    {   
        @Override
        public void actionPerformed(ActionEvent e) 
        {
            try
            {
                for(int i=0;i<=10;i++)
                {
                    final int percent = i;
                    SwingUtilities.invokeAndWait(new Runnable() 
                    {   
                        @Override
                        public void run()
                        {
                            frame.getProgressMonitor().setProgress(percent * 10);
                            frame.getProgressMonitor().setNote("Completed " + percent*10 + "%.");
                        }
                    });
                    try
                    {
                        Thread.sleep(1000);
                    }
                    catch(Exception ee)
                    {
                        //
                    }                           
                }
            }
            catch(Exception es)
            {
                //
            }
        }           
    });
}

/**
 * Create the frame.
 */
public ProgressMonitorTest() 
{
    isFrameReady = false;

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 450, 300);
    setTitle("Progress Monitor");

    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    contentPane.setLayout(new BorderLayout(0, 0));

    progressMonitor = new ProgressMonitor(frame, "Update in progress...", "", 0, 10);
    button = new JButton("Click Here");
    contentPane.add(button);

    setContentPane(contentPane);
}

}

A few questions regarding this-

  1. If I remove the isFrameReady check, the program says a NullPointerException at the line where I assign the button's action listener.

  2. If I keep the above check, then clicking on the button does nothing.

  3. Keeping the above check and then debugging this, I let it wait for some time before it gets to the line where the action listener. In this case, it works but immediately quits saying it can't call invokeAndWait from the event handling thread.

What am I missing in all this ? Can someone explain how to get this to work.


Solution

  • If I remove the isFrameReady check, the program says a NullPointerException at the line where I assign the button's action listener.

    your use of isFrameReady ensures that you have created your frame successfully. inside your main, your posted request to event dispatch thread(EDT) using call EventQueue.invokeLater(new Runnable(){}): removing the check isFrameReady, you were going to call frame.getButton() in main thread but the frame have not been yet created by frame = new ProgressMonitorTest(); in the EDT and thus a NullPointerException occurs.

    If I keep the above check, then clicking on the button does nothing.

    you should understand by now, that above check is nothing to do with button click. The button is not doing anything because the GUI got freezed for violating swing's single threading rule. Put your incrementing for loop of the actionPerformed method inside another thread as the following code fragement shows and execute it from there. you will see that it works fine.

     new Thread(){
        public void run()
        {
           for(int i=0; i<10; i++)
            {
               //whatever you were doing.
            }
        }
       }.start(); 
    

    Keeping the above check and then debugging this, I let it wait for some time before it gets to the line where the action listener. In this case, it works but immediately quits saying it can't call invokeAndWait from the event handling thread.

    SwingUtitlies.invokeAndWait() blocks the current thread and waits until the EDT is done executing the task given to it. As actionPerformed() function is already running inside EDT, so calling SwingUtitlies.invokeAndWait() from the current thread:EDT would block the current thread:EDT which should not be allowed. Don't use invokeAndWait for this case. you should call SwingUtilities.invokeLater() instead.

    However I don't think you will get anything until you understand Swing threading model. Read the javadoc and some internet resource. DO HAVE The book Filthy Rich Clients and try the example the book offered: You will have a greater knowledge in graphical effects then any other resource can provide.