简体   繁体   English

无法从SwingWorker类更新JProgressBar

[英]Can't get JProgressBar to update from SwingWorker class

I have my main GUI thread which has a JprogressBar in it and is implementing ProprtyChangeListener. 我有我的主GUI线程,其中包含JprogressBar并正在实现ProprtyChangeListener。

When a button is pressed, a different class, which extends SwingWorker, kicks into action and performs a series of potentially long calculations. 当按下一个按钮时,一个不同的类(扩展SwingWorker)会启动并执行一系列可能很长的计算。 I need the progress bar in class A to present the progress according to a variable in Class B. 我需要A类中的进度条根据B类变量显示进度。

My code is below (could be a bit messy with all my failed tries...) 我的代码在下面(对于我所有失败的尝试都可能有点凌乱......)

Would appreciate any help. 非常感谢任何帮助。

GUI CLASS: 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(); 

Calculating class: 计算类:

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

EDIT 编辑

Original problem remains. 原始问题仍然存在 For some reason the progress bar is still not updating, I know for sure that the "progress" variable in progressBar.setValue(progress) is updating yet the progress bar in the GUI remains unchanged (fixed at 0) here is my new code: 由于某种原因,进度条仍未更新,我确信progressBar.setValue(progress)中的“progress”变量正在更新,但GUI中的进度条保持不变(固定为0)这里是我的新代码:

GUI Class: 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();

SwingWorker Class: SwingWorker类:

@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;           

EDIT Rewrote the whole thing without the extra (irrelevant) code. 编辑重写了整个事情,没有额外的(不相关的)代码。 I guess I'm missing something basic here caus it still doesn't update the progress bar. 我想我遗漏了一些基本的东西因为它仍然没有更新进度条。

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

}

}

And the SwingWorker: 和SwingWorker:

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

}

What am I doing wrong here? 我在这做错了什么?

You are calling doInBackground() directly from your code, something that is akin to calling run() directly in a runnable. 您直接从代码中调用doInBackground() ,这类似于直接在runnable中调用run() This means that your code is not in fact running on a background thread, and so you are likely clobbering the event thread with long running code, preventing the Swing GUI, and your progress bar, from updating. 这意味着您的代码实际上并不在后台线程上运行,因此您可能会使用长时间运行的代码来破坏事件线程,从而阻止Swing GUI和进度条进行更新。

Solution: don't do this. 解决方案:不要这样做。 Call execute() on your worker when you want it to run. 如果希望它运行,请调用worker上的execute()

If you need further help, you're going to first have to help us. 如果您需要进一步的帮助,您首先必须帮助我们。 You understand that you have a ton of code posted, most of it completely unrelated to your problem at hand, and certainly more than you should ask volunteers to go through. 您了解到您发布了大量代码,其中大部分代码与您手头的问题完全无关,而且肯定会超过您应该让志愿者通过的代码。 Please get rid of all the extraneous unrelated code, and instead create and post a proper MCVE . 请摆脱所有无关的无关代码,而是创建并发布一个合适的MCVE


Edit 编辑
You also appear to be calling code directly from the EDT that should be left to your worker thread here: 您似乎也直接从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);

You also appear to be creating only one worker object which is not proper since you can't re-use a SwingWorker object. 您似乎也只创建了一个不正确的工作对象,因为您无法重用SwingWorker对象。

I suggest that you only create your SwingWorker object when it is needed, that you pass the information for what type of signal is needed into its constructor. 我建议您只在需要时创建SwingWorker对象,将所需信号类型的信息传递给构造函数。 This way the above methods can be called from the SwingWorker's doInBackground method where they belong. 这样,可以从SwingWorker的doInBackground方法调用上述方法。

eg, 例如,

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

Note that you have some other significant unrelated problems in the code you've posted, but they will have to be addressed at some other time, but they include use of null layout and setBounds, almost always a very bad idea. 请注意,您发布的代码中还有其他一些重要的无关问题,但是必须在其他时间解决它们,但它们包括使用null布局和setBounds,几乎总是一个非常糟糕的主意。


Edit 编辑

Just to clarify once again, your main problem is that you're calling long-running code on the Swing event thread. 再说一遍,你的主要问题是你在Swing事件线程上调用长时间运行的代码。 Just because a method is located in your Worker class does not mean that calling it will automatically have it run on a background thread. 仅仅因为一个方法位于您的Worker类中并不意味着调用它会自动让它在后台线程上运行。 The only way to guarantee this is to have the code called from within your doInBackground() method. 保证这一点的唯一方法是在doInBackground()方法中调用代码。 Again, what you want to do is to create your new worker object when it is needed , for instance, inside of some ActionListener, and at the time of its creation, pass into it all the information that it will need to run. 同样,您要做的是在需要时创建新的worker对象,例如,在某个ActionListener内部,并在创建时将所有需要运行的信息传递给它。 Then add your PropertyChangeListener, then .execute() your worker. 然后添加您的PropertyChangeListener,然后添加.execute()您的工作者。 Do this, and I'll bet your code will work much better. 这样做,我敢打赌你的代码会更好。


Edit 编辑
For example 例如

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

Edit 编辑
Regarding your new code, you've got two main problems: 关于您的新代码,您有两个主要问题:

Look at the results of your data output: 查看数据输出的结果:

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

At the pace that this is going, your progress value will reach 1 and cause a visible change to the PropertyChangeListener and the JProgressBar when the next ice age is upon us. 按照这样的速度,您的进度值将达到1,并在下一个冰河时代到来时对PropertyChangeListener和JProgressBar进行可见的更改。 So first of all, change your sleep times, and change your big number to something more reasonable. 首先,改变你的睡眠时间,并将你的大号改为更合理的东西。

Next, you shadow important variables, notably your JProgressBar variable, progressBar. 接下来,您将影响重要变量,尤其是JProgressBar变量progressBar。 Here is where you declare it and initialize it in the class: 您可以在此处声明它并在类中初始化它:

public class ProgressBarTest implements PropertyChangeListener {

   private JFrame frame;
   private JButton btnRun;
   static JProgressBar progressBar = new JProgressBar(0, 100);

As a side note, this variable should most definitely not be declared static , but that's not the cause of your current problem. 作为旁注,这个变量绝对不应该被声明为static ,但这不是你当前问题的原因。 The cause is that you in fact re-declare the same variable elsewhere in your initialize method, and then add this new object into your GUI: 原因是您实际上在initialize方法的其他地方重新声明了相同的变量,然后将这个新对象添加到GUI中:

private void initialize() {
  frame = new JFrame();

  // .....

  JProgressBar progressBar = new JProgressBar();

  // .....

  frame.getContentPane().add(progressBar);

Please understand that this new progressBar variable references a completely different JProgressBar, and so if you advance the value of the object created in the class, your GUI will show nothing because it is displaying a completely different object. 请理解这个新的progressBar变量引用了一个完全不同的JProgressBar,因此如果你提升在类中创建的对象的值,你的GUI将不会显示任何内容,因为它显示的是完全不同的对象。 To solve this, **don't redeclare and initialize a new variable in the initialize method. 要解决此问题,**不要在initialize方法中重新声明并初始化新变量。 Instead use the object created in the class. 而是使用在类中创建的对象。

Other problems with your code: you use null layout and setBounds a lot. 您的代码的其他问题:您使用null布局和setBounds很多。 This will show to all that you are a newbie Swing programmer, since it means that you like to create rigid programs that are extremely difficult to upgrade, and that may not look good on all systems. 这将向所有人表明你是一个新手Swing程序员,因为这意味着你喜欢创建极难升级的严格程序,并且在所有系统上可能看起来不太好。 Instead use the layout managers. 而是使用布局管理器。 For instance, here's your code with a few changes, all noted by comments: 例如,这里是您的代码,有一些更改,所有注释都注明:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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