无法从 SwingWorker 类更新 JProgressBar



我有一个主GUI线程,其中有一个JprogressBar,并且正在实现ProprtyChangeListener。

当按下一个按钮时,另一个扩展SwingWorker的类开始工作,并执行一系列可能很长的计算。我需要A类中的进度条来根据B类中的变量显示进度。

我的代码如下(可能有点混乱,因为我所有的失败尝试…)

如果有任何帮助,我将不胜感激。

GUI类:

SignalSimulator signalSimulator = new SignalSimulator(
                path, numOfdataPoints, numOfLocalSpikes,
                    numOfExpSpikes, noiseAmp, slope, offset,
                                    rdbtnSineWave.isSelected());       
signalSimulator.addPropertyChangeListener( new PropertyChangeListener() {
    public void propertyChange(PropertyChangeEvent evt) {
        String property = evt.getPropertyName();
        if ("progress".equals(property)) {
            int progress = (Integer) evt.getNewValue();
            System.out.println("value in PropertChangeListener is: " + progress);
            progressBar.setValue(progress);
        }
    }
});
signalSimulator.execute(); 

计算类别:

protected Integer doInBackground() throws Exception {
    if (isSine){
        data = generateSineWave(numOfDataPoints, noiseAmp, offset);
        data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
    } else {
        data = generateLinearSignal(numOfDataPoints, noiseAmp, slope, offset);
        data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
    }
    writeLogFile(path, ".txt", data);
    firePropertyChange("progress", 1, 1);
    setProgress((int)progress);
    publish(progress);
    System.out.println("value in doInBackground is: " + progress);
    return 1;
}

编辑

最初的问题仍然存在。由于某些原因,进度条仍然没有更新,我确信progressBar.setValue(progress)中的"progress"变量正在更新,但GUI中的进度条保持不变(固定为0)这是我的新代码:

GUI类:

    SignalSimulator signalSimulator = new SignalSimulator(path, numOfdataPoints, numOfLocalSpikes, numOfExpSpikes, noiseAmp, slope, offset, rdbtnSineWave.isSelected());       
    signalSimulator.addPropertyChangeListener( new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        String property = evt.getPropertyName();
        if ("progress".equals(property)) {
            int progress = (Integer) evt.getNewValue();
             System.out.println("value in PropertChangeListener is: " + progress);
             progressBar.setValue(progress);
        }
      }
    });
    signalSimulator.execute();

摇摆工人阶级:

@Override
        protected Integer doInBackground() throws Exception {
            if (isSine){
                data = generateSineWave(numOfDataPoints, noiseAmp, offset);
                data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
            }
            else{
                data = generateLinearSignal(numOfDataPoints, noiseAmp, slope, offset);
                data = addAnomalies(data, numOfPointOutliers, numOfExpSpikes);
            }
            writeLogFile(path, ".txt", data);
            return 1;}

public double[] generateSineWave(int numOfDataPoints, double noiseAmp, double offset){
            Random rnd = new Random();
            double[] dataArray = new double[numOfDataPoints];           

            for (int i=0;i<numOfDataPoints;i++){
                dataArray[i] =  Math.sin(Math.toRadians(i))+rnd.nextDouble()*noiseAmp+offset;
                progress = ((double)i)/(double)numOfDataPoints*100;
                //firePropertyChange("progress", 1, 1);
                setProgress((int)progress);
                //publish(progress);
                System.out.println("value in doInBackground is: " + progress);
            }
            return dataArray;           

编辑在没有额外(无关)代码的情况下重新编写整个内容。我想我错过了一些基本的东西,因为它仍然没有更新进度条。

public class ProgressBarTest implements PropertyChangeListener {

    private JFrame frame;
    private JButton btnRun;
    static JProgressBar progressBar = new JProgressBar(0,100);
public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable()  {
            public void run() {
                try {
                    //UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"); 
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); 
                    ProgressBarTest window = new ProgressBarTest();
                    window.frame.setVisible(true);
                    //SignalSimulator signalSimulator = new SignalSimulator();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
/**
 * Create the application.
 */
public ProgressBarTest() {
    initialize();
}
private void initialize() {
    frame = new JFrame();
    frame.setBounds(100, 100, 450, 300);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(null);
    frame.setResizable(false);  
    JProgressBar progressBar = new JProgressBar();
    progressBar.setAlignmentX(Component.RIGHT_ALIGNMENT);
    progressBar.setBounds(0, 252, 444, 20);
    progressBar.setStringPainted(true);
    frame.getContentPane().add(progressBar);

    JButton btnRun = new JButton("Start Long Run");
    btnRun.setBounds(167, 214, 159, 31);
    frame.getContentPane().add(btnRun);
    btnRun.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            longRun();
        }
    } );
}

private void longRun(){
    LongRunner longRunner = new LongRunner(100000);
    longRunner.addPropertyChangeListener(new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())){
                int progress = (int) evt.getNewValue();
                System.out.println("Value in propertyChangeListener: "+progress);
                progressBar.setValue(progress);
            }
        }
    });
    longRunner.execute();
}
@Override
public void propertyChange(PropertyChangeEvent arg0) {
    // TODO Auto-generated method stub
}
}

和摇摆工人:

import javax.swing.SwingWorker;

public class LongRunner extends SwingWorker<Integer, Double>{
    int numOfPoints;
    double progress;
    public LongRunner(int numOfPoints) {
        this.numOfPoints = numOfPoints;
        this.progress = 0;
    }
    private void runLong(int bigNum){
        for (int i=0; i< bigNum; i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            progress =  (((double)i*100)/(double)bigNum);
            setProgress((int)progress);
            System.out.println("Value in setProgress: "+progress);
        }
    }
    @Override
    protected Integer doInBackground() throws Exception {
        runLong(numOfPoints);
        return null;
    }
}

我在这里做错了什么?

您直接从代码中调用doInBackground(),类似于在可运行程序中直接调用run()。这意味着您的代码实际上并不是在后台线程上运行的,因此您可能会用长时间运行的代码破坏事件线程,从而阻止Swing GUI和进度条的更新。

解决方案:不要这样做。当您希望工作人员运行execute()时,请对其进行调用。

如果你需要进一步的帮助,你首先必须帮助我们。你知道你发布了大量的代码,其中大部分与你手头的问题完全无关,当然比你应该要求志愿者完成的还要多。请去掉所有无关的代码,创建并发布一个合适的MCVE。


编辑
您似乎还直接从EDT调用代码,这些代码应该留给您的工作线程:

        signalSimulator.execute();
        // ************* all these signalSimulator calls below ***********
        if (rdbtnSineWave.isSelected()) {
           data = signalSimulator.generateSineWave(numOfdataPoints,
                 noiseAmp, offset);
           data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
                 numOfExpSpikes);
        } else { // Linear signal is selected
           data = signalSimulator.generateLinearSignal(numOfdataPoints,
                 noiseAmp, slope, offset);
           data = signalSimulator.addAnomalies(data, numOfLocalSpikes,
                 numOfExpSpikes);
        }
        signalSimulator.writeLogFile(path, ".txt", data);

您似乎只创建了一个工作对象,这是不正确的,因为您不能重用SwingWorker对象。

我建议您只在需要的时候创建SwingWorker对象,并将所需信号类型的信息传递到其构造函数中。通过这种方式,可以从SwingWorker的doInBackground方法调用上述方法。

例如

signalSimulator = SignalSimulator(rdbtnSineWave.isSelected())
signalSimulator.addPropertyChangeListener(...);
signalSimulator.execute();

请注意,在您发布的代码中还有一些其他重要的无关问题,但它们必须在其他时间解决,但它们包括使用null布局和setBounds,这几乎总是一个非常糟糕的主意。


编辑

再次澄清一下,您的主要问题是在Swing事件线程上调用长时间运行的代码。方法位于Worker类中并不意味着调用它会自动在后台线程上运行。保证这一点的唯一方法是从doInBackground()方法中调用代码。同样,您想要做的是在需要时创建新的工作者对象,例如,在某个ActionListener内部,并在创建时将运行所需的所有信息传递给它。然后添加您的PropertyChangeListener,然后添加.execute()您的工作者。这样做,我敢打赌你的代码会更好地工作。


编辑
例如

import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class ProgressExampleGui {
   private JPanel mainPanel = new JPanel();
   private JProgressBar progressBar = new JProgressBar();
   private JButton pressMeBtn  = new JButton(new MyAction("Press Me", KeyEvent.VK_P, this));
   public ProgressExampleGui() {
      progressBar.setStringPainted(true);
      progressBar.setString("");
      mainPanel.add(pressMeBtn);
      mainPanel.add(progressBar);
   }
   public void setProgress(int progress) {
      progressBar.setValue(progress);
      progressBar.setString(progress + "%");
   }
   public JComponent getMainComponent() {
      return mainPanel;
   }
   public void setEnabled(boolean enabled) {
      pressMeBtn.setEnabled(enabled);
   }
   private static void createAndShowGui() {
      ProgressExampleGui progExampleGui = new ProgressExampleGui();
      JFrame frame = new JFrame("Progress Example");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(progExampleGui.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
@SuppressWarnings("serial")
class MyAction extends AbstractAction {
   private ProgressExampleGui gui;
   public MyAction(String name, int mnemonic, ProgressExampleGui gui) {
      super(name);
      putValue(MNEMONIC_KEY, mnemonic);
      this.gui = gui;
   }
   @Override
   public void actionPerformed(ActionEvent e) {
      AbstractButton source = (AbstractButton) e.getSource();
      gui.setProgress(0);
      source.setEnabled(false);
      MyWorker myWorker = new MyWorker();
      myWorker.addPropertyChangeListener(new WorkerPropChngListener(gui));
      myWorker.execute();
   }
}
class WorkerPropChngListener implements PropertyChangeListener {
   private ProgressExampleGui gui;
   public WorkerPropChngListener(ProgressExampleGui gui) {
      this.gui = gui;
   }
   @Override
   public void propertyChange(PropertyChangeEvent pcEvt) {
      MyWorker myWorker = (MyWorker) pcEvt.getSource();
      if ("progress".equals(pcEvt.getPropertyName())) {
         int progress = ((Integer)pcEvt.getNewValue()).intValue();
         gui.setProgress(progress);
      }
      if (SwingWorker.StateValue.DONE.equals(pcEvt.getNewValue())) {
         try {
            myWorker.get();
         } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
         }
         gui.setEnabled(true);
      }
   }
}
class MyWorker extends SwingWorker<Void, Void> {
   private static final int MAX_INCR = 8;
   private static final long SLEEP_TIME = 200;
   private static final int MAX_VALUE = 100;
   private int value = 0;
   private Random random = new Random();
   @Override
   protected Void doInBackground() throws Exception {
      while (value < MAX_VALUE) {
         value += random.nextInt(MAX_INCR);
         value = Math.min(value, MAX_VALUE);
         Thread.sleep(SLEEP_TIME);
         setProgress(value);
      }
      return null;
   }
}

编辑
关于您的新代码,您有两个主要问题:

查看数据输出的结果:

Value in setProgress: 0.0
Value in setProgress: 0.001
Value in setProgress: 0.002
Value in setProgress: 0.003
Value in setProgress: 0.004
Value in setProgress: 0.005
Value in setProgress: 0.006
Value in setProgress: 0.007
Value in setProgress: 0.008
Value in setProgress: 0.009
Value in setProgress: 0.01
Value in setProgress: 0.011
Value in setProgress: 0.012
Value in setProgress: 0.013
Value in setProgress: 0.014
Value in setProgress: 0.015
Value in setProgress: 0.016
Value in setProgress: 0.017
Value in setProgress: 0.018
Value in setProgress: 0.019
Value in setProgress: 0.02
Value in setProgress: 0.021
Value in setProgress: 0.022
Value in setProgress: 0.023
Value in setProgress: 0.024
Value in setProgress: 0.025
Value in setProgress: 0.026
Value in setProgress: 0.027
Value in setProgress: 0.028
Value in setProgress: 0.029

按照这样的速度,你的进度值将达到1,并在下一个冰河时期到来时对PropertyChangeListener和JProgressBar造成可见的更改。因此,首先,改变你的睡眠时间,将你的大数字更改为更合理的数字。

接下来,隐藏重要的变量,特别是JProgressBar变量progressBar。以下是您在类中声明并初始化它的位置:

public class ProgressBarTest implements PropertyChangeListener {
   private JFrame frame;
   private JButton btnRun;
   static JProgressBar progressBar = new JProgressBar(0, 100);

顺便说一句,这个变量绝对不应该声明为static,但这不是当前问题的原因。原因是您实际上在初始化方法的其他地方重新声明相同的变量,然后将这个新对象添加到GUI中:

private void initialize() {
  frame = new JFrame();
  // .....
  JProgressBar progressBar = new JProgressBar();
  // .....
  frame.getContentPane().add(progressBar);

请理解,这个新的progressBar变量引用了一个完全不同的JProgressBar,因此,如果您提高类中创建的对象的值,您的GUI将不会显示任何内容,因为它显示的是一个完全相同的对象。要解决此问题,**不要在initialize方法中重新声明和初始化新变量。而是使用在类中创建的对象。

代码的其他问题:您经常使用null布局和setBounds。这将向所有人表明,你是一个新手Swing程序员,因为这意味着你喜欢创建非常难以升级的僵化程序,而且可能在所有系统上都不太好。而是使用布局管理器。例如,以下是您的代码,其中有一些更改,所有更改都通过注释进行了说明:

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
//!! no need to implement PropertyChangeListener
//!! public class ProgressBarTest implements PropertyChangeListener {
public class ProgressBarTest2 {
   private JFrame frame;
   private JButton btnRun;
   // !! this shouldn't be static!
   // !! static JProgressBar progressBar = new JProgressBar(0, 100);
   private JProgressBar progressBar = new JProgressBar(0, 100); // !!
   public static void main(String[] args) {
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            try {
               UIManager
                     .setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
               ProgressBarTest2 window = new ProgressBarTest2();
               window.frame.setVisible(true);
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });
   }
   public ProgressBarTest2() {
      initialize();
   }
   private void initialize() {
      frame = new JFrame();
      // !!frame.setBounds(100, 100, 450, 300);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      //!! frame.getContentPane().setLayout(null); //!! never use null layouts
      frame.setResizable(false);
      // !! don't create a shadowed variable
      // !! JProgressBar progressBar = new JProgressBar();
      progressBar.setAlignmentX(Component.RIGHT_ALIGNMENT);
      //!! progressBar.setBounds(0, 252, 444, 20);
      progressBar.setStringPainted(true);
      //!! frame.getContentPane().add(progressBar);
      frame.getContentPane().add(progressBar, BorderLayout.SOUTH);
      btnRun = new JButton("Start Long Run"); //!! no shadowing
      //!! btnRun.setBounds(167, 214, 159, 31);
      JPanel panel = new JPanel(); //!!
      panel.setPreferredSize(new Dimension(450, 300)); //!!
      panel.setLayout(new GridBagLayout()); //!!
      panel.add(btnRun); //!!
      frame.getContentPane().add(panel, BorderLayout.CENTER); //!!
      btnRun.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            longRun();
         }
      });
      //!!
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }
   private void longRun() {
    //!! use a more realistic value, one that should show change in listener
      //!! LongRunner longRunner = new LongRunner(100000);
      LongRunner2 longRunner = new LongRunner2(10000);  
      longRunner.addPropertyChangeListener(new PropertyChangeListener() {
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
               int progress = (int) evt.getNewValue();
               System.out.println("Value in propertyChangeListener: "
                     + progress);
               progressBar.setValue(progress);
            }
         }
      });
      longRunner.execute();
   }
   // !! @Override // !! not needed
   // public void propertyChange(PropertyChangeEvent evt) {
   // }
}
class LongRunner2 extends SwingWorker<Integer, Double> {
   private static final long SLEEP_TIME = 15; // !!
   int numOfPoints;
   double progress;
   public LongRunner2(int numOfPoints) {
      this.numOfPoints = numOfPoints;
      this.progress = 0;
   }
   private void runLong(int bigNum) {
      for (int i = 0; i < bigNum; i++) {
         try {
            // !! quicker turn-over so that our bigNum can change
            // in a realistic way
            // !! Thread.sleep(100);
            Thread.sleep(SLEEP_TIME);// !!
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         progress = (((double) i * 100) / (double) bigNum);
         setProgress((int) progress);
         // !! System.out.println("Value in setProgress: " + progress); //!! This will slow us down
      }
   }
   @Override
   protected Integer doInBackground() throws Exception {
      runLong(numOfPoints);
      return null;
   }
}

最新更新