views:

112

answers:

2

I'm creating an application in Swing using NetBeans. I would like to be able to manipulate some components during its startup (just once), after the window's made visible, for example update a progress bar. To this end, I have the app's main class, called MainWindow:

public class MainWindow extends JFrame
{

public MainWindow()
{
    initComponents(); // NetBeans GUI builder-generated function for setting
                      // up the window components

}

public void Init()
{
     loadLabel.setText("Loading....");
     loadProgressBar.setValue(20);
     doSomething();
     loadProgressBar.setValue(40);
     doSomething();
     loadProgressBar.setValue(80);
     doSomething();
     loadProgressBar.setValue(100);

     loadLabel.setVisible(false);
     loadProgressBar.setVisible(false);
}

/* .... */

public static void main(String args[]) 
{
    java.awt.EventQueue.invokeLater(new Runnable() 
    {
        public void run() 
        {
            mainHandle = new MainWindow();
            mainHandle.setVisible(true);
            mainHandle.Init();
        }
    });
}

}

The problem is that the effect of the statements for updating the progress bar (or manipulating any other GUI component) within the Init() function can't be observed. If the Init() function is called from within main() as shown above, the window appears, but is empty, the Init() function executes and returns, only afterwards the window draws its contents but any changes made by Init() aren't visible because the window was empty and inactive the whole time. I also tried calling init from the windowOpened() AWT event, which executes after the window is fully drawn, but amazingly putting any statements for manipulating components there seems to have no effect, or rather they are put in a queue, and executed rapidly at some point in succession, so only the effect of the last one (hiding of the elements) can be observed. The only way I managed to get it working was to remove the whole invokeLater(new Runnable()...) mantra and put the new MainWindow(), setVisible(), Init() sequence directly in main(), which I guess is very ugly and breaks the concept of the gui running in a threaded manner. What is the right way to do this? Where do I put code to be executed first thing when the gui is ready to be manipulated, execute the statements once and return control to the main event loop?

I guess at the moment this is working in such a way, that while the Init() function is operating, any operations on the gui components are suspended (the drawing thread isn't separate and waits for Init() to finish before the manipulations are executed). Maybe I should make Init() a new thread... only how and what kind?

Thanks.

A: 

You could change the EventQueue.invokeLater() to invokeAndWait(), and move the call to init() out to a second EventQueue.invokeLater() call.

If (as looks to be the case) doSomething() takes a noticable amount of time, a better idea is to move the Init code into the body of a SwingWorker. This could be executed from the MainWindow() constructor or after the setVisible() call in main and is the idiomatic way to have a responsive GUI (in case the user gets bored waiting and wants to quit) and display some visible signs of progress.

See the process and publish methods for details on how to update the progress bar between doSomething() calls.

You may also want to look into ProgressMonitors for another alternative that would deal with the dialog box etc for you.

MHarris
A: 

There are several things you can do:

  1. For windows (such as JFrame or JDialog) you can attach WindowListener and do your manipulations in windowOpened method.
  2. Override addNotify method and do your control manipulations there.
  3. Attach HierarchyListener and do your manipulations whenever displayability of component changed.

Always make sure your do your component manipulations on EDT. Use SwingUtilities.invokeLater for simple UI updates or SwingWorker for long running tasks

eugener
Ad 1.: I may be not getting something, but it seems to me that I remarked about trying that and it didn't work.
neuviemeporte
You can try any of 3 different methods I offered. I would use #2 in your case and simply call init from addNotify( don't forget to call super.addNotify )
eugener