Domanda

I have two threads. One is running the business logic from my BulkProcessor class which updates the BulkProcessor.getPercentComplete() variable:

public void run(){
   new SwingWorker<Void,Void>() {
      protected Void doInBackground() throws Exception {
         BulkProcessor.main(jTextField0.getText(), jTextField1.getText());
         return null;
      };
   }.execute();
}

My other thread is what updates the jProgressBar's value in my BulkGUI class:

public void update(){
   jProgressBar0.setStringPainted(true);
   jProgressBar0.repaint();
   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();
}

I call the two threads when the Process button is clicked:

private void jButton0ActionActionPerformed(ActionEvent event) {
run();
update();
}

Running this the first time works exactly as expected. However selecting the Process button a second time has no affect on the jProgressBar. The local variable percentComplete in the update thread remains at 100, and does not update as it does the first run. I tested the percentComplete variable from the BulkProcessor class, and this variable does in fact update as expected. So for some reason the thread is not fetching the update values using BulkProcessor.getPercentComplete() the second time the thread is called. Anybody have any insight on this? Any help is much appreciated.

È stato utile?

Soluzione

I'm guessing that your problem is likely because percentComplete is maximum the second time you press your button. Suggestions:

  • reset percentComplete each time the code is run.
  • Avoid making Swing calls from within a background thread, including reading textfield text.
  • Rather do this in the SwingWorker's constructor, not in its doInBackground(...) method.
  • You appear to be making static field and/or method calls. If so, don't as this will bite you in the end, and instead create and pass valid references.
  • I'm guessing that to do this, you will want to create a new BulkProcessor object.
  • Calling run() and then update() as you're doing, getting two threads to run concurrently looks dangerous to me, a possible set up for a race condition.

Edit

You should look into using a SwingWorker's progress property with a PropertyChangeListener. For instance:

public class FooGUI {

   // .....

   public void myRun() {
      String text1 = someTextField.getText();
      String text2 = otherTextField.getText();
      final BulkProcessor bulkProcessor = new BulkProcessor(text1, text2);
      bulkProcessor.addPropertyChangeListener(new PropertyChangeListener() {

         @Override
         public void propertyChange(PropertyChangeEvent pcEvt) {
            if (pcEvt.getPropertyName().equals("progress")) {
               int progress = bulkProcessor.getProgress();
               someProgressBar.setValue(progress);
            }
         }
      });
   }
}

class BulkProcessor extends SwingWorker<Void, Void> {
   private Random random = new Random(); // just for SSCCE sake
   private String text1;
   private String text2;

   public BulkProcessor(String text1, String text2) {
      this.text1 = text1;
      this.text2 = text2;
      // not sure what you do with these texts....
   }

   @Override
   protected Void doInBackground() throws Exception {
      int progress = 0;
      while (progress <= 100) {
         progress = random.nextInt(5); // just as a for instance
                                    // your code will do something else of course
         setProgress(progress);
         Thread.sleep(300);
      }
      return null;
   }

}

For example:

import java.awt.event.*;
import java.beans.*;
import java.util.Random;
import javax.swing.*;

@SuppressWarnings("serial")
public class Foo3 extends JPanel {
   private static final String DEFAULT_SPEED = "15";
   private JTextField speedTextField = new JTextField(DEFAULT_SPEED, 5);
   private JProgressBar someProgressBar = new JProgressBar();
   private RunAction runAction = new RunAction();
   private JButton runButton = new JButton(runAction);

   public Foo3() {
      speedTextField.setAction(runAction);

      add(new JLabel("Speed:"));
      add(speedTextField);
      add(someProgressBar);
      add(runButton);
   }

   public void myRun() {
      String speedText = speedTextField.getText();
      try {
         int speed = Integer.parseInt(speedText);
         final BulkProcessor bulkProcessor = new BulkProcessor(speed);
         bulkProcessor.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent pcEvt) {
               if (pcEvt.getPropertyName().equals("progress")) {
                  int progress = bulkProcessor.getProgress();
                  someProgressBar.setValue(progress);
               }
               if (pcEvt.getPropertyName().equals("state")) {
                  if (bulkProcessor.getState().equals(
                        SwingWorker.StateValue.DONE)) {
                     someProgressBar.setValue(0);
                     setGuiEnabled(true);
                  }
               }
            }
         });
         setGuiEnabled(false);
         bulkProcessor.execute();
      } catch (NumberFormatException e) {
         String text = "Speed of " + speedTextField.getText()
               + " is invalid. Please enter an integer";

         JOptionPane.showMessageDialog(this, text, "Invalid Speed Value",
               JOptionPane.ERROR_MESSAGE);
         speedTextField.setText(DEFAULT_SPEED);
      }
   }

   private class RunAction extends AbstractAction {
      public RunAction() {
         super("Run");
         putValue(MNEMONIC_KEY, KeyEvent.VK_R);
      }

      @Override
      public void actionPerformed(ActionEvent arg0) {
         myRun();
      }

   }

   private void setGuiEnabled(boolean enabled) {
      runButton.setEnabled(enabled);
      speedTextField.setEnabled(enabled);
   }

   private static void createAndShowGui() {
      Foo3 mainPanel = new Foo3();

      JFrame frame = new JFrame("Foo3");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class BulkProcessor extends SwingWorker<Void, Void> {
   private Random random = new Random(); // just for SSCCE sake
   private int speed;

   public BulkProcessor(int speed) {
      this.speed = speed;
   }

   @Override
   protected Void doInBackground() throws Exception {
      int progress = 0;
      while (progress <= 100) {
         progress += random.nextInt(speed);
         setProgress(progress);
         Thread.sleep(300);
      }
      return null;
   }

}

Altri suggerimenti

So found that changing while(percentComplete < 100) to while(percentComplete <= 100) keeps the thread running permanently. This actually fixes the issue, might not be recommended however.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top