2

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

}
Zoyd
  • 3,449
  • 1
  • 18
  • 27
TaylorSmolik
  • 116
  • 3
  • 13
  • 1
    Don't block the EDT (Event Dispatch Thread) - the GUI will 'freeze' when that happens. Instead of calling `Thread.sleep(n)` implement a Swing `Timer` for repeating tasks or a `SwingWorker` for long running tasks. See [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) for more details. ..OK that is a comment I posted, based on the title. But.. `Thread.sleep(100);` *inside* a `SwingWorker`? That is weirdness personified! For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Jun 13 '13 at 14:14
  • This is a [duplicate](http://stackoverflow.com/questions/8251607/running-a-jframe-with-a-jprogressbar). – Uwe Plonus Jun 13 '13 at 14:16
  • Please see the above Edit, I based this off of Uwe Plonus' duplicate link – TaylorSmolik Jun 13 '13 at 15:02
  • [for example](http://stackoverflow.com/search?q=user%3A714968+[swingworker]+jprogressbar), some are under JTable tag, there are examples with SwingWoker and Runnable@Thread – mKorbel Jun 18 '13 at 19:46
  • different _class_ is not a problem as long as the threads interact correctly – kleopatra Jun 21 '13 at 14:23
  • Not sure what the issue could be then – TaylorSmolik Jun 21 '13 at 14:28
  • wild guess: the thread interaction :-) Try to put together a SSCCE that demonstrates the problem – kleopatra Jun 21 '13 at 16:23
  • What are the types of `countRecord`and `totalRecord` in `BulkProcessor`? – Harald K Jun 24 '13 at 14:39

4 Answers4

0

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.

user1676075
  • 3,056
  • 1
  • 19
  • 26
  • % complete that you have in the while loop is that referring to my getPercentComplete()? – TaylorSmolik Jun 17 '13 at 20:24
  • Also, doesn't there need to be some return type? – TaylorSmolik Jun 17 '13 at 20:44
  • There is some cleanup needed. I was just trying to hit the main points. You don't really need to return anything (although you do need a return type, yes, though Void/null is valid). The publish(Integer) and thus process(Integer) are the main ones for handling the status updates. process() will actually get List and you'll just want item 0. Yes, as for the % complete, you need some way to know when to stop checking for and posting progress updates. But presumably you have that info (checking for progress < 100 is just one way). – user1676075 Jun 17 '13 at 21:29
  • In your example, the process method is never read (look at my last edit for what code I used) – TaylorSmolik Jun 18 '13 at 19:19
  • 1
    See my edits: 1) Changing while loop to do-while, with read of bulk processor value _inside_ the loop. 2) Add @Override annotation to the process() method to make sure you get the signature correct (which should be a list of integers, you only care about the first one). – user1676075 Jun 18 '13 at 20:53
  • I updated my edit to match your edits. It runs, but it jumps to 100% at the end of the task similar to before. – TaylorSmolik Jun 18 '13 at 21:20
  • I'd suggest after getting the value from BulkProcessing (inside the SwingWorker) you dump it to a log/system.out. The SwingWorker isn't guaranteed to start right away, and you'll want to make sure the BulkProcessor really is updating correctly in this use case. So a dump of values would let you know if it _should_ be updating. If all you get is 100% in a log/output, then it's very likely updating correctly, but just not quickly enough. If you're getting more logs/outputs (within that function) than just 100%, some more code examples may be necessary. – user1676075 Jun 19 '13 at 20:09
  • Also, make sure your BulkProcessor isn't updating on the Swing thread (is your application completely unresponsive while it updates)? If that's doing its processing on the swing thread, no method will cause your GUI to update. The fix to that would be running the updates to BulkProcessor (whatever it's doing) on a background thread (hopefully that's already the case). – user1676075 Jun 19 '13 at 20:11
  • You said you're sure the BulkProcessor is updating correctly (you get values between 0 and 100)... something is missing in your sample code. The main() calls BulkProcessor.process(), but it's not clear where the Swing GUI comes into play, or how those interact. Is it a different process? If it's the same process, where do you start Swing, and then how do you start BulkProcessor.process() ? Still sounds like either BulkProcessor isn't updating right, or BulkProcessor.process() is running in the Swing thread (the latter seems most likely). – user1676075 Jun 21 '13 at 16:40
  • It interacts with the Swing GUI with the BulkProcessor.getPercentComplete(); in my update() method. All relevant code from the GUI class is posted. – TaylorSmolik Jun 21 '13 at 17:01
  • bulk.process() is the larger method, while inside of it is the getPercentComplete() method, could this be the issue? – TaylorSmolik Jun 21 '13 at 17:25
  • I have to believe, at this point, that your BulkProcessor is performing its calculations/updates in the Swing thread (likely main thread). Swing updates from a queue... so you can post changes to swing widgets, but they don't do anything until the current operation completes. If you're bulk processor does its main work in the swing thread, nothing will ever change, no matter what else you do, until that returns. It sounds like you need to run the bulk processor in the doInBackground of a swing worker or other thread. – user1676075 Jun 24 '13 at 13:06
0

The mistake you probably went on is calling the t.start(); after thejButton0ActionPerformed(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.

0

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. :-)

Harald K
  • 26,314
  • 7
  • 65
  • 111
0

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.

Mr.Mountain
  • 863
  • 8
  • 32