JProgressBar не будет обновляться, пока процесс не будет завершен

Я знаю, что здесь тонны JProgressBar вопросов, но, судя по всем ответам, я не могу диагностировать свою проблему. Я обрабатываю файл с помощью некоторого программного обеспечения для проверки адреса. Я нажимаю кнопку «Обработать», и мне нужно, чтобы мой JProgressBar обновлялся с каждым обрабатываемым файлом. Вот кнопка:

private JButton getJButton0() {
...
   jButton0.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent event) {
         jButton0ActionActionPerformed(event);
         t.start();
      }
...

По рекомендации всех, я использовал метод setValue() в потоке

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() — это метод, который я вызываю из другого класса, который представляет процент выполнения. Я протестировал этот метод, и он корректно обновляется. Проблема в том, что индикатор выполнения не будет обновляться до тех пор, пока файлы не будут обработаны, а затем он поднимется до 100%. Прошу прощения, если это повторяющийся вопрос, но я безуспешно копался на этом сайте. Любая помощь очень ценится.

Изменить:

В соответствии с рекомендуемым дубликатом я пробовал это:

public void update(){
   new SwingWorker<Void,Void>() {
   protected Void doInBackground() throws Exception {
   jProgressBar0.setValue(BulkProcessor.getPercentComplete());
   return null;
 };
 }.execute();
}

А затем попытался вызвать этот метод update() под actionPerformed() (переключил t.start() на update()). У меня все еще та же проблема.

Изменить

На основании рекомендации пользователя 1676075, однако та же проблема:

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

Изменить

Вот код из моего класса BulkProcessor

 private String getOutputLine( String searchString, String inputLine )
throws QasException
{
 ..(code for processing lines)..
 countRecord++;
    percentComplete = (int) Math.round((countRecord/totalRecord)*100);

totalRecord обновляется в основном классе моего класса BulkProcessor

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

}

person TaylorSmolik    schedule 13.06.2013    source источник
comment
Не блокируйте EDT (поток отправки событий) — когда это произойдет, графический интерфейс «зависнет». Вместо вызова Thread.sleep(n) реализуйте Swing Timer для повторяющихся задач или SwingWorker для длительных задач. Дополнительные сведения см. в разделе Параллелизм в Swing. ..Хорошо, это комментарий, который я разместил, основываясь на заголовке. Но.. Thread.sleep(100); внутри SwingWorker? Это воплощение странности! Чтобы быстрее получить помощь, опубликуйте SSCCE.   -  person Andrew Thompson    schedule 13.06.2013
comment
Это дубликат.   -  person Uwe Plonus    schedule 13.06.2013
comment
Пожалуйста, смотрите приведенное выше редактирование, я основал это на дублирующей ссылке Уве Плонуса.   -  person TaylorSmolik    schedule 13.06.2013
comment
например, некоторые находятся под тегом JTable, есть примеры с SwingWoker и Runnable@Thread   -  person mKorbel    schedule 18.06.2013
comment
другой класс не является проблемой, если потоки взаимодействуют корректно   -  person kleopatra    schedule 21.06.2013
comment
тогда не знаю в чем может быть дело   -  person TaylorSmolik    schedule 21.06.2013
comment
дикая догадка: взаимодействие потоков :-) Попробуйте составить SSCCE, демонстрирующий проблему   -  person kleopatra    schedule 21.06.2013
comment
Каковы типы countRecordи totalRecord в BulkProcessor?   -  person Harald K    schedule 24.06.2013


Ответы (4)


Похоже, вы смешиваете обычаи. См. документацию SwingWorker, пример вверху: http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html.

В идеале вы должны обновить свой BulkProcessor в методе doInBackground SwingWorker, и это вызовет setProgress, а jProgressBar будет прослушивать эти обновления прогресса, как в примере.

Если это не сработает для вас, что, похоже, не будет основано только на вышеизложенном, запустите SwingWorker из события нажатия кнопки. Реализуйте методы SwingWorker примерно так (псевдокод):

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

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

person user1676075    schedule 17.06.2013
comment
% завершения, которое у вас есть в цикле while, относится к моему getPercentComplete()? - person TaylorSmolik; 18.06.2013
comment
Кроме того, не должен ли быть какой-то возвращаемый тип? - person TaylorSmolik; 18.06.2013
comment
Требуется некоторая очистка. Я просто пытался затронуть основные моменты. Вам действительно не нужно ничего возвращать (хотя вам нужен тип возвращаемого значения, да, хотя Void/null допустим). Публикация (целое число) и, следовательно, процесс (целое число) являются основными для обработки обновлений статуса. process() на самом деле получит List‹Integer›, и вам просто нужен элемент 0. Да, что касается % завершения, вам нужен какой-то способ узнать, когда прекратить проверку и публикацию обновлений прогресса. Но, по-видимому, у вас есть эта информация (проверка прогресса ‹ 100 — это только один из способов). - person user1676075; 18.06.2013
comment
В вашем примере метод процесса никогда не читается (посмотрите мое последнее редактирование, какой код я использовал) - person TaylorSmolik; 18.06.2013
comment
См. мои правки: 1) Замена цикла while на do-while с чтением объемного значения процессора внутри цикла. 2) Добавьте аннотацию @Override к методу process(), чтобы убедиться, что вы получили правильную подпись (которая должна быть списком целых чисел, вас интересует только первое). - person user1676075; 19.06.2013
comment
Я обновил свою правку, чтобы она соответствовала вашей правке. Он работает, но подскакивает до 100% в конце задачи, как и раньше. - person TaylorSmolik; 19.06.2013
comment
Я бы посоветовал после получения значения от BulkProcessing (внутри SwingWorker) выгрузить его в log/system.out. Не гарантируется, что SwingWorker запустится сразу, и вы должны убедиться, что BulkProcessor действительно корректно обновляется в этом случае использования. Таким образом, дамп значений позволит вам узнать, следует ли обновляться. Если все, что вы получаете, это 100% в журнале/выводе, то, скорее всего, оно обновляется правильно, но недостаточно быстро. Если вы получаете больше журналов/выходных данных (внутри этой функции), чем просто 100%, может потребоваться еще несколько примеров кода. - person user1676075; 20.06.2013
comment
Кроме того, убедитесь, что ваш BulkProcessor не обновляется в потоке Swing (ваше приложение полностью не отвечает во время обновления)? Если он выполняет свою обработку в потоке качания, ни один метод не заставит ваш графический интерфейс обновляться. Исправление этого будет запускать обновления для BulkProcessor (что бы он ни делал) в фоновом потоке (надеюсь, это уже так). - person user1676075; 20.06.2013
comment
Вы сказали, что уверены, что BulkProcessor обновляется правильно (вы получаете значения от 0 до 100)... в вашем примере кода чего-то не хватает. main() вызывает BulkProcessor.process(), но неясно, где в игру вступает графический интерфейс Swing или как они взаимодействуют. Это другой процесс? Если это один и тот же процесс, где вы запускаете Swing и как вы запускаете BulkProcessor.process()? По-прежнему звучит так, будто либо BulkProcessor не обновляется правильно, либо BulkProcessor.process() работает в потоке Swing (последнее кажется наиболее вероятным). - person user1676075; 21.06.2013
comment
Он взаимодействует с графическим интерфейсом Swing с помощью BulkProcessor.getPercentComplete(); в моем методе update(). Весь соответствующий код из класса GUI публикуется. - person TaylorSmolik; 21.06.2013
comment
bulk.process() - это более крупный метод, а внутри него находится метод getPercentComplete(), может ли это быть проблемой? - person TaylorSmolik; 21.06.2013
comment
На данный момент я должен верить, что ваш BulkProcessor выполняет свои вычисления/обновления в потоке Swing (вероятно, в основном потоке). Свинг-обновления из очереди... так что вы можете отправлять изменения в свинг-виджеты, но они ничего не делают, пока не завершится текущая операция. Если у вас объемный процессор выполняет свою основную работу в качающемся потоке, ничего никогда не изменится, что бы вы еще ни делали, пока это не вернется. Похоже, вам нужно запустить массовый процессор в doInBackground свинг-воркера или другого потока. - person user1676075; 24.06.2013

Ошибка, которую вы, вероятно, допустили, заключается в вызове t.start(); после thejButton0ActionPerformed(event); что означает, что после выполнения действия поток запустится. Поэтому значение индикатора выполнения не обновляется должным образом.

Вам нужно запустить поток в jButton0ActionPerformed(event); а затем обновить значение в нем.

person Community    schedule 23.06.2013
comment
Вы правы, я понял это раньше, но не обновил это в своем посте, извините. - person TaylorSmolik; 24.06.2013

Просто догадка, но...

    percentComplete = (int) Math.round((countRecord/totalRecord)*100);

Вы уверены, что это не целочисленная арифметика? Я не знаю тип totalRecord, поэтому не могу сказать наверняка.

Я предполагаю, что все работает нормально, и просто прогресс всегда равен 0, пока не завершится, где он волшебным образом равен 100. Это потому, что целое число, разделенное на целое, не будет иметь дробных значений (т.е. 99/100 == 0, 100/100 == 1). Это полностью согласуется с симптомами, которые вы испытываете.

Попробуйте заменить строку выше на:

   percentComplete = (int) Math.round((countRecord/(double) totalRecord)*100);

чтобы увидеть это я прав. :-)

person Harald K    schedule 24.06.2013
comment
Я сделал system.out.print(percentComplete) и распечатал нужные мне значения в консоли (1,3,4,5,......100). Так что я не верю, что это будет проблемой. - person TaylorSmolik; 24.06.2013
comment
Кроме того, totalRecord является двойным - person TaylorSmolik; 24.06.2013

Вы пытались использовать интерфейс PropertyChangeListener? ?

Вычисления будут выполняться потоком Swingworker, а основной графический интерфейс будет реализовывать этот интерфейс. Некоторый пример кода

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

Используя метод "setProgress" рабочего потока свинга, основной поток графического интерфейса будет уведомлен, если что-то произошло.

@Override
public void propertyChange(PropertyChangeEvent property) {
     Integer currentValue = new Integer(0);
     currentValue = (Integer) property.getNewValue();
     this.progressBar.setValue(currentValue.intValue());
}

Swing не является потокобезопасным. Это не лучшее решение, но, возможно, оно может вам помочь. Пожалуйста, прокомментируйте, если есть что-то ужасное не так.

person Mr.Mountain    schedule 24.06.2013