简体   繁体   中英

JProgressBar will not update until process is completed

Tons of JProgressBar questions on here I know, but through all the answers and I can't seem to diagnose my issue. I am processing a file with some address verification software. I click the Process button and I need my JProgressBar to update with each file processed. Here is the button:

private JButton getJButton0() {
...
   jButton0.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
         jButton0ActionActionPerformed(event);
         t.start();
      }
...

Per everybody's recommendation, I used the setValue() method within a thread

Thread t = new Thread(){
    public void run() {
    SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        jProgressBar0.setValue(BulkProcessor.getPercentComplete());
    }
});
try {
    Thread.sleep(100);
} catch (InterruptedException e) {
}
...

BulkProcessor.getPercentComplete() is a method I'm calling from another class which represents the percentage complete. I have tested this method and it updates correctly. The issue is that the progress bar will not update until the files are finished processing, and then it will jump to 100%. I apologize if this is a repeat question, but I have done some serious digging on this site with no luck. Any help much appreciated.

Edit:

Per recommended duplicate, I tried this:

public void update(){
   new SwingWorker<Void,Void>() {
   protected Void doInBackground() throws Exception {
   jProgressBar0.setValue(BulkProcessor.getPercentComplete());
   return null;
 };
 }.execute();
}

And then tried calling this update() method under the actionPerformed() (switched t.start() with update() ). I am still having the same issue.

Edit

Based on user1676075's recommendation, however same issue:

    public static void update(){
       new SwingWorker<Void,Integer>() {
       protected Void doInBackground() throws Exception {
           do
           {
           percentComplete = BulkProcessor.getPercentComplete();
           publish(percentComplete);
           Thread.sleep(100);
           } while(percentComplete < 100);

        return null;
       }
       @Override
    protected
       void process(List<Integer> progress)
       {
           jProgressBar0.setValue(progress.get(0));
       }
     }.execute();
   }

Edit

Here is the code from my BulkProcessor class

 private String getOutputLine( String searchString, String inputLine )
throws QasException
{
 ..(code for processing lines)..
 countRecord++;
    percentComplete = (int) Math.round((countRecord/totalRecord)*100);

totalRecord is updated in the main class of my BulkProcessor class

 public static void main( String input, String output ){
    count.clear();
    try{
        String inputFile = input;
        String outputFile = output;
        LineNumberReader  lnr = new LineNumberReader(new FileReader(new File(input)));
        lnr.skip(Long.MAX_VALUE);
        totalRecord = lnr.getLineNumber() + 1; //line count in file
        BulkProcessor bulk = new BulkProcessor(inputFile, outputFile, ConfigManager.DFLT_NAME);
        bulk.process();
    }catch(Exception e ){
        e.printStackTrace();
    }

}

Looks like you're mixing usages. See the SwingWorker documentation, example at the top: http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html .

Ideally you'd update your BulkProcessor in the doInBackground method of the SwingWorker, and that would call setProgress, and the jProgressBar would be listening for those progress updates as in the example.

If that won't work for you, which it seems like it won't just based on the above, start a SwingWorker from the button press event. Implement the SwingWorker methods kinda like this (pseudocode):

new SwingWorker<Void,Integer>()
{
  doInBackground()
  {
    do
    {
      percentComplete = BulkProcessor.getPercentComplete();
      publish(percentCompete);
      Thread.sleep(100);
    } while (percentComplete < 100);
  }

  @Override
  process(List<Integer> progress)
  {
     jProgressBar0.setValue(progress.get(0));
  }
}.execute();

You'll need to add error-handling and checks for complete and failure cases, but that should get you started and to where you want to be. doInBackground runs in a background thread so won't block anything, and process() runs on the swing worker thread so will post the updates.

The mistake you probably went on is calling the t.start(); after the jButton0ActionPerformed(event); which makes that after the action is performed the thread will start. Therefore the value of the progress bar is not updated as intended.

You need to start the thread in jButton0ActionPerformed(event); and then update the value in it.

Just a hunch, but...

    percentComplete = (int) Math.round((countRecord/totalRecord)*100);

Are you sure this is not integer arithmetic? I don't know the type of totalRecord , so I can't say for sure.

I'd guess everything works fine, and just the progress is 0 all the time, until complete where it magically is 100. This is because an int divided by an int will not have fraction values (ie. 99/100 == 0, 100/100 == 1). This fits perfectly with the symptoms you are experiencing.

Try replacing the line above with:

   percentComplete = (int) Math.round((countRecord/(double) totalRecord)*100);

to see it I'm right. :-)

Have you tried to use the PropertyChangeListener -interface?

The calculations will be done by the Swingworker-thread and the main-gui will implement this interface. Some example-code

@Override
public void actionPerformed(ActionEvent e) {
    this.myButton.setEnabled(false);
    MyWorkerThread thread = new MyWorkerThread(); //Data-processing
    thread.addPropertyChangeListener(this.mainguiframe); //Separation of concern
    thread.execute();
}

Using the "setProgress"-method of the swing-worker-thread the main-gui-thread will be notified if something has happend.

@Override
public void propertyChange(PropertyChangeEvent property) {
     Integer currentValue = new Integer(0);
     currentValue = (Integer) property.getNewValue();
     this.progressBar.setValue(currentValue.intValue());
}

Swing is not thread-safe. This is not the best solution but perhaps it can help you. Please comment if there is somethin horrible wrong.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM