2014-09-05 3 views
1

У меня есть мой основной поток графического интерфейса, в котором есть JprogressBar и реализуется ProprtyChangeListener.Не удается получить JProgressBar для обновления из класса SwingWorker

При нажатии кнопки другой класс, который расширяет SwingWorker, запускается в действие и выполняет ряд потенциально длинных вычислений. мне нужен прогресс бар в классе А, чтобы представить прогресс в соответствии с переменной в классе 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; 
} 

EDIT

Оригинал проблема остается. По какой-то причине индикатор выполнения все еще не обновляется, Я точно знаю, что переменная «progress» в progressBar.setValue (прогресс) обновляется, но индикатор выполнения в 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(); 

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 переписаны все дело без дополнительного (нерелевантного) кода. Наверное, мне не хватает чего-то основного здесь, потому что он все еще не обновляет индикатор выполнения.

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 

} 

} 

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

} 

Что я здесь делаю неправильно?

+0

Вы затеняете несколько переменных, а самое главное, переменную JProgressBar. Вы также создаете данные, которые прогрессируют в ледяном темпе, которые никогда не будут показывать прирост int в этой жизни. Обратите внимание, что ваши значения прогресса меньше 1. Это никогда не будет отображаться в индикаторе выполнения. –

ответ

3

Вы вызываете doInBackground() прямо из своего кода, что-то похожее на вызов run() непосредственно в исполняемом файле. Это означает, что ваш код на самом деле не работает в фоновом потоке, и поэтому вы, вероятно, будете клонировать поток событий с длинным запущенным кодом, предотвращая обновление графического интерфейса Swing и вашего индикатора выполнения.

Решение: не делайте этого. Вызовите 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 только тогда, когда это необходимо, чтобы передать информацию о том, какой тип сигнала необходим в его конструкторе. Таким образом, вышеупомянутые методы можно вызвать из метода doInBackground SwingWorker, где они принадлежат.

например,

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

Обратите внимание, что у вас есть какие-то другие важные проблемы, не связанные в коде, вы в курсе, но они должны будут решаться в другое время, но они включают в себя использование нулевой компоновки и setBounds, почти всегда очень плохая идея.


Редактировать

Просто еще раз уточнить, ваша главная проблема заключается в том, что вы звоните, затянувшийся код на резьбе Качели событий. Просто потому, что метод находится в вашем классе 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 , поэтому, если вы продвигаете значение объекта, созданного в классе, ваш графический интерфейс не будет показывать ничего, потому что он отображает совершенно другой объект. Чтобы решить эту проблему, ** не обновляйте и не инициализируйте новую переменную в методе initialize. Вместо этого используйте объект, созданный в классе.

Другие проблемы с вашим кодом: вы используете нулевой макет и 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; 
    } 
} 
+0

Спасибо за подробный ответ !!! просто чтобы убедиться, что я правильно понимаю: 1. Весь длинный код вычисления в SwingWorker должен быть в области doInBackground? 2. все параметры к нему должны быть переданы в конструкторе? 3. инициировать расчет через excute(), а не прямой вызов типа signalSimulator.generateSignal()? –

+0

Итак, я сделал вышеупомянутые изменения и добавил System.out.println как в doInBackground(), так и в PropertyChangeListener. это единственный результат, который я получаю: значение в doInBackground: 99.9 Значение в PropertChangeListener: 99. индикатор выполнения не обновляется :(отправил новые фрагменты кода и –

+0

Еще одно изменение - я помещаю firePropertyChange() в цикл, который выполняет все длинные вычисления, и теперь я вижу в System.out, что ProprtyChangeListener действительно слушает еще прогресс бар до сих пор не обновляется: «код» значение в doInBackground составляет: 14,7 значения в doInBackground является: 14,799999999999999 значения в doInBackground является: 14,899999999999999 значения в PropertChangeListener является: 9 значения в PropertChangeListener составляет: 14 значения в doInBackground является : 15.0 Значение в doInBackground: 15.1 –