views:

252

answers:

3

How can I update the JProgressBar.setValue(int) from another thread? My secondary goal is do it in the least amount of classes possible.

Here is the code I have right now:

**Part of the main class....**
pp.addActionListener(
                new ActionListener(){
                    public void actionPerformed(ActionEvent event)
                    {
                            new Thread(new Task(sd.getValue())).start(); 
                    }
                });

public class Task implements Runnable{
int val;
public Task(int value){
    this.val = value;
}
@Override
public void run() {
    for (int i=0; i<=value; i++){ //Progressively increment variable i 
        pbar.setValue(i); //Set value 
        pbar.repaint(); //Refresh graphics 
        try{Thread.sleep(50);} //Sleep 50 milliseconds 
        catch (InterruptedException err){} 
    } 
    }
}

pp is a JButton and starts the new thread when the JButton is clicked.

pbar is the JProgressBar object from the Main class.

How can I update its value?(progress)

The code above in run() cannot see the pbar.

A: 

You shouldn't do any Swing stuff outside of the event dispatch thread. To access this, you need to create a Runnable with your code in run, and then pass that off to SwingUtilities.invokeNow() or SwingUtilities.invokeLater(). The problem is that we need a delay in your JProgressBar checking to avoid jamming up the Swing thread. To do this, we'll need a Timer which will call invokeNow or later in its own Runnable. Have a look at http://www.javapractices.com/topic/TopicAction.do?Id=160 for more details.

Chris Dennett
A: 
  • There is need not to call pbra.repaint explicitly.
  • Update JProgressBar shall be done through GUI dispatch thread.

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        // Remember to make pbar final variable.
        pbar.setValue(i);
    }
});
Yan Cheng CHEOK
+1  A: 

Always obey swing's rule

Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.

What you can do is to create an observer that will update your progress bar -such as - in this instance you want to show progress of data being loaded on click of a button. DemoHelper class implements Observable and sends updates to all observers on when certain percent of data is loaded. Progress bar is updated via public void update(Observable o, Object arg) {

class PopulateAction implements ActionListener, Observer {

    JTable tableToRefresh;
    JProgressBar progressBar;
    JButton sourceButton;
    DemoHelper helper;
    public PopulateAction(JTable tableToRefresh, JProgressBar progressBarToUpdate) {
        this.tableToRefresh = tableToRefresh;
        this.progressBar = progressBarToUpdate;
    }

    public void actionPerformed(ActionEvent e) {
        helper = DemoHelper.getDemoHelper();
        helper.addObserver(this);
        sourceButton = ((JButton) e.getSource());
        sourceButton.setEnabled(false);
        helper.insertData();
    }

    public void update(Observable o, Object arg) {
        progressBar.setValue(helper.getPercentage());
    }
}

Shameless plug: this is from source from my demo project Feel free to browse for more details.

ring bearer